// Implementation: impl Responder
// File: crates/router/src/analytics.rs
// Module: router
// Methods: 0 total (0 public)

impl Responder


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

pub fn resend_invite(
    state: SessionState,
    user_from_token: auth::UserFromToken,
    request: user_api::ReInviteUserRequest,
    auth_id: Option<String>,
) -> UserResponse<()>


// Struct: EbanxPayoutResponse
// File: crates/hyperswitch_connectors/src/connectors/ebanx/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct EbanxPayoutResponse


// Implementation: impl ConnectorPreAuthentication for for Netcetera
// File: crates/hyperswitch_connectors/src/connectors/netcetera.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl ConnectorPreAuthentication for for Netcetera


    }
    fn get_routing_config_cache_key(self) -> storage_impl::redis::cache::CacheKind<'static> {
        let merchant_id = self.profile.merchant_id.clone();

        let profile_id = self.profile.get_id().to_owned();

        storage_impl::redis::cache::CacheKind::Routing(
            format!(
                "routing_config_{}_{}",
                merchant_id.get_string_repr(),
                profile_id.get_string_repr()
            )
            .into(),
        )
    }

    pub async fn update_profile_and_invalidate_routing_config_for_active_algorithm_id_update(
        self,
        db: &dyn StorageInterface,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        algorithm_id: id_type::RoutingId,
        transaction_type: &storage::enums::TransactionType,
    ) -> RouterResult<()> {
        let routing_cache_key = self.clone().get_routing_config_cache_key();

        let (routing_algorithm_id, payout_routing_algorithm_id) = match transaction_type {
            storage::enums::TransactionType::Payment => (Some(algorithm_id), None),
            #[cfg(feature = "payouts")]
            storage::enums::TransactionType::Payout => (None, Some(algorithm_id)),
            //TODO: Handle ThreeDsAuthentication Transaction Type for Three DS Decision Rule Algorithm configuration
            storage::enums::TransactionType::ThreeDsAuthentication => todo!(),
        };

        let profile_update = domain::ProfileUpdate::RoutingAlgorithmUpdate {
            routing_algorithm_id,
            payout_routing_algorithm_id,
        };

        let profile = self.profile;

        db.update_profile_by_profile_id(
            key_manager_state,
            merchant_key_store,
            profile,
            profile_update,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to update routing algorithm ref in business profile")?;

        storage_impl::redis::cache::redact_from_redis_and_publish(
            db.get_cache_store().as_ref(),
            [routing_cache_key],
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to invalidate routing cache")?;
        Ok(())
    }

    pub fn get_routing_algorithm_id<'a>(
        &'a self,
        transaction_data: &'a routing::TransactionData<'_>,
    ) -> Option<id_type::RoutingId> {
        match transaction_data {
            routing::TransactionData::Payment(_) => self.profile.routing_algorithm_id.clone(),
            #[cfg(feature = "payouts")]
            routing::TransactionData::Payout(_) => self.profile.payout_routing_algorithm_id.clone(),
        }
    }
    pub fn get_default_fallback_list_of_connector_under_profile(
        &self,
    ) -> RouterResult<Vec<routing_types::RoutableConnectorChoice>> {
        let fallback_connectors =
            if let Some(default_fallback_routing) = self.profile.default_fallback_routing.clone() {
                default_fallback_routing
                    .expose()
                    .parse_value::<Vec<routing_types::RoutableConnectorChoice>>(
                        "Vec<RoutableConnectorChoice>",
                    )
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Business Profile default config has invalid structure")?
            } else {
                Vec::new()
            };
        Ok(fallback_connectors)
    }
    pub fn get_default_routing_configs_from_profile(
        &self,
    ) -> RouterResult<routing_types::ProfileDefaultRoutingConfig> {
        let profile_id = self.profile.get_id().to_owned();
        let connectors = self.get_default_fallback_list_of_connector_under_profile()?;

        Ok(routing_types::ProfileDefaultRoutingConfig {
            profile_id,
            connectors,
        })
    }

    pub async fn update_default_fallback_routing_of_connectors_under_profile(
        self,
        db: &dyn StorageInterface,
        updated_config: &Vec<routing_types::RoutableConnectorChoice>,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> RouterResult<()> {
        let default_fallback_routing = Secret::from(
            updated_config
                .encode_to_value()
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to convert routing ref to value")?,
        );
        let profile_update = domain::ProfileUpdate::DefaultRoutingFallbackUpdate {
            default_fallback_routing: Some(default_fallback_routing),
        };

        db.update_profile_by_profile_id(
            key_manager_state,
            merchant_key_store,
            self.profile,
            profile_update,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to update routing algorithm ref in business profile")?;
        Ok(())
    }
    pub async fn update_revenue_recovery_algorithm_under_profile(
        self,
        db: &dyn StorageInterface,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        revenue_recovery_retry_algorithm_type: common_enums::RevenueRecoveryAlgorithmType,
    ) -> RouterResult<()> {
        let recovery_algorithm_data =
            diesel_models::business_profile::RevenueRecoveryAlgorithmData {
                monitoring_configured_timestamp: date_time::now(),
            };
        let profile_update = domain::ProfileUpdate::RevenueRecoveryAlgorithmUpdate {
            revenue_recovery_retry_algorithm_type,
            revenue_recovery_retry_algorithm_data: Some(recovery_algorithm_data),
        };

        db.update_profile_by_profile_id(
            key_manager_state,
            merchant_key_store,
            self.profile,
            profile_update,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable(
            "Failed to update revenue recovery retry algorithm in business profile",
        )?;
        Ok(())
    }
}

pub async fn extended_card_info_toggle(
    state: SessionState,
    merchant_id: &id_type::MerchantId,
    profile_id: &id_type::ProfileId,
    ext_card_info_choice: admin_types::ExtendedCardInfoChoice,
) -> RouterResponse<admin_types::ExtendedCardInfoChoice> {
    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,
            merchant_id,
            &state.store.get_master_key().to_vec().into(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)
        .attach_printable("Error while fetching the key store by merchant_id")?;

    let business_profile = db
        .find_business_profile_by_profile_id(key_manager_state, &key_store, profile_id)
        .await
        .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
            id: profile_id.get_string_repr().to_owned(),
        })?;

    if business_profile.is_extended_card_info_enabled.is_none()
        || business_profile
            .is_extended_card_info_enabled
            .is_some_and(|existing_config| existing_config != ext_card_info_choice.enabled)
    {
        let profile_update = domain::ProfileUpdate::ExtendedCardInfoUpdate {
            is_extended_card_info_enabled: ext_card_info_choice.enabled,
        };

        db.update_profile_by_profile_id(
            key_manager_state,
            &key_store,
            business_profile,
            profile_update,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
            id: profile_id.get_string_repr().to_owned(),
        })?;
    }

    Ok(service_api::ApplicationResponse::Json(ext_card_info_choice))
}

pub async fn connector_agnostic_mit_toggle(
    state: SessionState,
    merchant_id: &id_type::MerchantId,
    profile_id: &id_type::ProfileId,
    connector_agnostic_mit_choice: admin_types::ConnectorAgnosticMitChoice,
) -> RouterResponse<admin_types::ConnectorAgnosticMitChoice> {
    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,
            merchant_id,
            &state.store.get_master_key().to_vec().into(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)
        .attach_printable("Error while fetching the key store by merchant_id")?;

    let business_profile = db
        .find_business_profile_by_profile_id(key_manager_state, &key_store, profile_id)
        .await
        .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
            id: profile_id.get_string_repr().to_owned(),
        })?;

    if business_profile.merchant_id != *merchant_id {
        Err(errors::ApiErrorResponse::AccessForbidden {
            resource: profile_id.get_string_repr().to_owned(),
        })?
    }

    if business_profile.is_connector_agnostic_mit_enabled
        != Some(connector_agnostic_mit_choice.enabled)
    {
        let profile_update = domain::ProfileUpdate::ConnectorAgnosticMitUpdate {
            is_connector_agnostic_mit_enabled: connector_agnostic_mit_choice.enabled,
        };

        db.update_profile_by_profile_id(
            key_manager_state,
            &key_store,
            business_profile,
            profile_update,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
            id: profile_id.get_string_repr().to_owned(),
        })?;
    }

    Ok(service_api::ApplicationResponse::Json(
        connector_agnostic_mit_choice,
    ))
}

pub async fn transfer_key_store_to_key_manager(
    state: SessionState,
    req: admin_types::MerchantKeyTransferRequest,
) -> RouterResponse<admin_types::TransferKeyResponse> {
    let resp = transfer_encryption_key(&state, req).await?;

    Ok(service_api::ApplicationResponse::Json(
        admin_types::TransferKeyResponse {
            total_transferred: resp,
        },
    ))
}

async fn process_open_banking_connectors(
    state: &SessionState,
    merchant_id: &id_type::MerchantId,
    auth: &types::ConnectorAuthType,
    connector_type: &api_enums::ConnectorType,
    connector: &api_enums::Connector,
    additional_merchant_data: types::AdditionalMerchantData,
    key_store: &domain::MerchantKeyStore,
) -> RouterResult<types::MerchantRecipientData> {
    let new_merchant_data = match additional_merchant_data {
        types::AdditionalMerchantData::OpenBankingRecipientData(merchant_data) => {
            if connector_type != &api_enums::ConnectorType::PaymentProcessor {
                return Err(errors::ApiErrorResponse::InvalidConnectorConfiguration {
                    config:
                        "OpenBanking connector for Payment Initiation should be a payment processor"
                            .to_string(),
                }
                .into());
            }
            match &merchant_data {
                types::MerchantRecipientData::AccountData(acc_data) => {
                    core_utils::validate_bank_account_data(acc_data)?;

                    let connector_name = api_enums::Connector::to_string(connector);

                    let recipient_creation_not_supported = state
                        .conf
                        .locker_based_open_banking_connectors
                        .connector_list
                        .contains(connector_name.as_str());
                    let recipient_id = if recipient_creation_not_supported {
                        locker_recipient_create_call(state, merchant_id, acc_data, key_store).await
                    } else {
                        connector_recipient_create_call(
                            state,
                            merchant_id,
                            connector_name,
                            auth,
                            acc_data,
                        )
                        .await
                    }
                    .attach_printable("failed to get recipient_id")?;

                    let conn_recipient_id = if recipient_creation_not_supported {
                        Some(types::RecipientIdType::LockerId(Secret::new(recipient_id)))
                    } else {
                        Some(types::RecipientIdType::ConnectorId(Secret::new(
                            recipient_id,
                        )))
                    };

                    let account_data = match &acc_data {
                        types::MerchantAccountData::Iban { iban, name, .. } => {
                            types::MerchantAccountData::Iban {
                                iban: iban.clone(),
                                name: name.clone(),
                                connector_recipient_id: conn_recipient_id.clone(),
                            }
                        }
                        types::MerchantAccountData::Bacs {
                            account_number,
                            sort_code,
                            name,
                            ..
                        } => types::MerchantAccountData::Bacs {
                            account_number: account_number.clone(),
                            sort_code: sort_code.clone(),
                            name: name.clone(),
                            connector_recipient_id: conn_recipient_id.clone(),
                        },
                        types::MerchantAccountData::FasterPayments {
                            account_number,
                            sort_code,
                            name,
                            ..
                        } => types::MerchantAccountData::FasterPayments {
                            account_number: account_number.clone(),
                            sort_code: sort_code.clone(),
                            name: name.clone(),
                            connector_recipient_id: conn_recipient_id.clone(),
                        },
                        types::MerchantAccountData::Sepa { iban, name, .. } => {
                            types::MerchantAccountData::Sepa {
                                iban: iban.clone(),
                                name: name.clone(),
                                connector_recipient_id: conn_recipient_id.clone(),
                            }
                        }
                        types::MerchantAccountData::SepaInstant { iban, name, .. } => {
                            types::MerchantAccountData::SepaInstant {
                                iban: iban.clone(),
                                name: name.clone(),
                                connector_recipient_id: conn_recipient_id.clone(),
                            }
                        }
                        types::MerchantAccountData::Elixir {
                            account_number,
                            iban,
                            name,
                            ..
                        } => types::MerchantAccountData::Elixir {
                            account_number: account_number.clone(),
                            iban: iban.clone(),
                            name: name.clone(),
                            connector_recipient_id: conn_recipient_id.clone(),
                        },
                        types::MerchantAccountData::Bankgiro { number, name, .. } => {
                            types::MerchantAccountData::Bankgiro {
                                number: number.clone(),
                                name: name.clone(),
                                connector_recipient_id: conn_recipient_id.clone(),
                            }
                        }
                        types::MerchantAccountData::Plusgiro { number, name, .. } => {
                            types::MerchantAccountData::Plusgiro {
                                number: number.clone(),
                                name: name.clone(),
                                connector_recipient_id: conn_recipient_id.clone(),
                            }
                        }
                    };

                    types::MerchantRecipientData::AccountData(account_data)
                }
                _ => merchant_data.clone(),
            }
        }
    };

    Ok(new_merchant_data)
}

async fn connector_recipient_create_call(
    state: &SessionState,
    merchant_id: &id_type::MerchantId,
    connector_name: String,
    auth: &types::ConnectorAuthType,
    data: &types::MerchantAccountData,
) -> RouterResult<String> {
    let connector = pm_auth_types::api::PaymentAuthConnectorData::get_connector_by_name(
        connector_name.as_str(),
    )?;

    let auth = pm_auth_types::ConnectorAuthType::foreign_try_from(auth.clone())
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed while converting ConnectorAuthType")?;

    let connector_integration: pm_auth_types::api::BoxedConnectorIntegration<
        '_,
        pm_auth_types::api::auth_service::RecipientCreate,
        pm_auth_types::RecipientCreateRequest,
        pm_auth_types::RecipientCreateResponse,
    > = connector.connector.get_connector_integration();

    let req = pm_auth_types::RecipientCreateRequest::from(data);
    let router_data = pm_auth_types::RecipientCreateRouterData {
        flow: std::marker::PhantomData,
        merchant_id: Some(merchant_id.to_owned()),
        connector: Some(connector_name),
        request: req,
        response: Err(pm_auth_types::ErrorResponse {
            status_code: http::StatusCode::INTERNAL_SERVER_ERROR.as_u16(),
            code: consts::NO_ERROR_CODE.to_string(),
            message: consts::UNSUPPORTED_ERROR_MESSAGE.to_string(),
            reason: None,
        }),
        connector_http_status_code: None,
        connector_auth_type: auth,
    };

    let resp = payment_initiation_service::execute_connector_processing_step(
        state,
        connector_integration,
        &router_data,
        &connector.connector_name,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed while calling recipient create connector api")?;

    let recipient_create_resp =
        resp.response
            .map_err(|err| errors::ApiErrorResponse::ExternalConnectorError {
                code: err.code,
                message: err.message,
                connector: connector.connector_name.to_string(),
                status_code: err.status_code,
                reason: err.reason,
            })?;

    let recipient_id = recipient_create_resp.recipient_id;

    Ok(recipient_id)
}
async fn locker_recipient_create_call(
    state: &SessionState,
    merchant_id: &id_type::MerchantId,
    data: &types::MerchantAccountData,
    key_store: &domain::MerchantKeyStore,
) -> RouterResult<String> {
    let key_manager_state = &state.into();
    let key = key_store.key.get_inner().peek();
    let identifier = km_types::Identifier::Merchant(key_store.merchant_id.clone());

    let data_json = serde_json::to_string(data)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to serialize MerchantAccountData to JSON")?;

    let encrypted_data = domain_types::crypto_operation(
        key_manager_state,
        type_name!(payment_method::PaymentMethod),
        domain_types::CryptoOperation::Encrypt(Secret::<String, masking::WithType>::new(data_json)),
        identifier,
        key,
    )
    .await
    .and_then(|val| val.try_into_operation())
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to encrypt merchant account data")?;

    let enc_data = hex::encode(encrypted_data.into_encrypted().expose());
    let merchant_id_string = merchant_id.get_string_repr().to_owned();

    let cust_id = id_type::CustomerId::try_from(std::borrow::Cow::from(merchant_id_string))
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to convert to CustomerId")?;

    let payload = transformers::StoreLockerReq::LockerGeneric(transformers::StoreGenericReq {
        merchant_id: merchant_id.to_owned(),
        merchant_customer_id: cust_id.clone(),
        enc_data,
        ttl: state.conf.locker.ttl_for_storage_in_secs,
    });

    let store_resp = cards::add_card_to_hs_locker(
        state,
        &payload,
        &cust_id,
        api_enums::LockerChoice::HyperswitchCardVault,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to encrypt merchant bank account data")?;

    Ok(store_resp.card_reference)
}

pub async fn enable_platform_account(
    state: SessionState,
    merchant_id: id_type::MerchantId,
) -> RouterResponse<()> {
    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,
            &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, &merchant_id, &key_store)
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

    db.update_merchant(
        key_manager_state,
        merchant_account,
        storage::MerchantAccountUpdate::ToPlatformAccount,
        &key_store,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Error while enabling platform merchant account")
    .map(|_| services::ApplicationResponse::StatusOk)
}

impl From<&types::MerchantAccountData> for pm_auth_types::RecipientCreateRequest {
    fn from(data: &types::MerchantAccountData) -> Self {
        let (name, account_data) = match data {
            types::MerchantAccountData::Iban { iban, name, .. } => (
                name.clone(),
                pm_auth_types::RecipientAccountData::Iban(iban.clone()),
            ),
            types::MerchantAccountData::Bacs {
                account_number,
                sort_code,
                name,
                ..
            } => (
                name.clone(),
                pm_auth_types::RecipientAccountData::Bacs {
                    sort_code: sort_code.clone(),
                    account_number: account_number.clone(),
                },
            ),
            types::MerchantAccountData::FasterPayments {
                account_number,
                sort_code,
                name,
                ..
            } => (
                name.clone(),
                pm_auth_types::RecipientAccountData::FasterPayments {
                    sort_code: sort_code.clone(),
                    account_number: account_number.clone(),
                },
            ),
            types::MerchantAccountData::Sepa { iban, name, .. } => (
                name.clone(),
                pm_auth_types::RecipientAccountData::Sepa(iban.clone()),
            ),
            types::MerchantAccountData::SepaInstant { iban, name, .. } => (
                name.clone(),
                pm_auth_types::RecipientAccountData::SepaInstant(iban.clone()),
            ),
            types::MerchantAccountData::Elixir {
                account_number,
                iban,
                name,
                ..
            } => (
                name.clone(),
                pm_auth_types::RecipientAccountData::Elixir {
                    account_number: account_number.clone(),
                    iban: iban.clone(),
                },
            ),
            types::MerchantAccountData::Bankgiro { number, name, .. } => (
                name.clone(),
                pm_auth_types::RecipientAccountData::Bankgiro(number.clone()),
            ),
            types::MerchantAccountData::Plusgiro { number, name, .. } => (
                name.clone(),
                pm_auth_types::RecipientAccountData::Plusgiro(number.clone()),
            ),
        };

        Self {
            name,
            account_data,
            address: None,
        }
    }
}


// Implementation: impl AcceptDispute for for Adyen
// File: crates/hyperswitch_connectors/src/connectors/adyen.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl AcceptDispute for for Adyen


// Function: get_profile_id
// File: crates/hyperswitch_domain_models/src/payments/payment_intent.rs
// Module: hyperswitch_domain_models

pub fn get_profile_id(&self) -> Option<id_type::ProfileId>


// Function: insert
// File: crates/diesel_models/src/query/dashboard_metadata.rs
// Module: diesel_models

pub fn insert(self, conn: &PgPooledConn) -> StorageResult<DashboardMetadata>


// Implementation: impl api::PaymentCapture for for Paysafe
// File: crates/hyperswitch_connectors/src/connectors/paysafe.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentCapture for for Paysafe


// Function: from_input
// File: crates/euclid/src/backend/vir_interpreter/types.rs
// Module: euclid

pub fn from_input(input: BackendInput) -> Self


// Function: get_not_implemented
// File: crates/hyperswitch_domain_models/src/router_data.rs
// Module: hyperswitch_domain_models

pub fn get_not_implemented() -> Self


// Struct: SuccessfulTransactionData
// File: crates/hyperswitch_connectors/src/connectors/riskified/transformers/api.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SuccessfulTransactionData


// Struct: BillingConnectorInvoiceSyncFlowData
// File: crates/hyperswitch_domain_models/src/router_data_v2/flow_common_types.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct BillingConnectorInvoiceSyncFlowData


// Function: generate_digest
// File: crates/hyperswitch_connectors/src/connectors/nordea.rs
// Module: hyperswitch_connectors

pub fn generate_digest(&self, payload: &[u8]) -> String


// Trait: IntoInjectorResponse
// File: crates/injector/src/types.rs
// Module: injector

pub trait IntoInjectorResponse


// Function: get_id
// File: crates/hyperswitch_domain_models/src/payments/payment_attempt.rs
// Module: hyperswitch_domain_models

pub fn get_id(&self) -> &str


// Function: get_payout_routing_algorithm
// File: crates/hyperswitch_domain_models/src/business_profile.rs
// Module: hyperswitch_domain_models

pub fn get_payout_routing_algorithm(
        &self,
    ) -> CustomResult<
        Option<api_models::routing::RoutingAlgorithmRef>,
        api_error_response::ApiErrorResponse,
    >


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

pub struct AdditionalErrorDetails


// Function: server
// File: crates/router/src/routes/app.rs
// Module: router

pub fn server(config: AppState) -> Scope


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

pub struct ToggleDynamicRoutingQuery


// Struct: OrgAuthenticationMethodUpdateInternal
// File: crates/diesel_models/src/user_authentication_method.rs
// Module: diesel_models
// Implementations: 0

pub struct OrgAuthenticationMethodUpdateInternal


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

pub struct SiftRouterData<T>


// Function: get_all_keys
// File: crates/euclid_wasm/src/lib.rs
// Module: euclid_wasm

pub fn get_all_keys() -> JsResult


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

impl api::RefundSync for for Moneris


// File: crates/analytics/src/payments/metrics/sessionized_metrics/payments_distribution.rs
// Module: analytics

use std::collections::HashSet;

use api_models::analytics::{
    payments::{PaymentDimensions, PaymentFilters, PaymentMetricsBucketIdentifier},
    Granularity, TimeRange,
};
use common_utils::errors::ReportSwitchExt;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

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

#[derive(Default)]
pub(crate) struct PaymentsDistribution;

#[async_trait::async_trait]
impl<T> super::PaymentMetric<T> for PaymentsDistribution
where
    T: AnalyticsDataSource + super::PaymentMetricAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    async fn load_metrics(
        &self,
        dimensions: &[PaymentDimensions],
        auth: &AuthInfo,
        filters: &PaymentFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(PaymentMetricsBucketIdentifier, PaymentMetricRow)>> {
        let mut query_builder: QueryBuilder<T> =
            QueryBuilder::new(AnalyticsCollection::PaymentSessionized);

        let mut dimensions = dimensions.to_vec();

        dimensions.push(PaymentDimensions::PaymentStatus);

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

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

        query_builder.add_select_column("first_attempt").switch()?;

        query_builder
            .add_select_column(Aggregate::Min {
                field: "created_at",
                alias: Some("start_bucket"),
            })
            .switch()?;
        query_builder
            .add_select_column(Aggregate::Max {
                field: "created_at",
                alias: Some("end_bucket"),
            })
            .switch()?;

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

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

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

        for dim in dimensions.iter() {
            query_builder
                .add_group_by_clause(dim)
                .attach_printable("Error grouping by dimensions")
                .switch()?;
        }
        query_builder
            .add_group_by_clause("first_attempt")
            .attach_printable("Error grouping by first_attempt")
            .switch()?;
        if let Some(granularity) = granularity {
            granularity
                .set_group_by_clause(&mut query_builder)
                .attach_printable("Error adding granularity")
                .switch()?;
        }

        query_builder
            .execute_query::<PaymentMetricRow, _>(pool)
            .await
            .change_context(MetricsError::QueryBuildingError)?
            .change_context(MetricsError::QueryExecutionFailure)?
            .into_iter()
            .map(|i| {
                Ok((
                    PaymentMetricsBucketIdentifier::new(
                        i.currency.as_ref().map(|i| i.0),
                        None,
                        i.connector.clone(),
                        i.authentication_type.as_ref().map(|i| i.0),
                        i.payment_method.clone(),
                        i.payment_method_type.clone(),
                        i.client_source.clone(),
                        i.client_version.clone(),
                        i.profile_id.clone(),
                        i.card_network.clone(),
                        i.merchant_id.clone(),
                        i.card_last_4.clone(),
                        i.card_issuer.clone(),
                        i.error_reason.clone(),
                        i.routing_approach.as_ref().map(|i| i.0.clone()),
                        i.signature_network.clone(),
                        i.is_issuer_regulated,
                        i.is_debit_routed,
                        TimeRange {
                            start_time: match (granularity, i.start_bucket) {
                                (Some(g), Some(st)) => g.clip_to_start(st)?,
                                _ => time_range.start_time,
                            },
                            end_time: granularity.as_ref().map_or_else(
                                || Ok(time_range.end_time),
                                |g| i.end_bucket.map(|et| g.clip_to_end(et)).transpose(),
                            )?,
                        },
                    ),
                    i,
                ))
            })
            .collect::<error_stack::Result<
                HashSet<(PaymentMetricsBucketIdentifier, PaymentMetricRow)>,
                crate::query::PostProcessingError,
            >>()
            .change_context(MetricsError::PostProcessingFailure)
    }
}


// Implementation: impl webhooks::IncomingWebhook for for Authorizedotnet
// File: crates/hyperswitch_connectors/src/connectors/authorizedotnet.rs
// Module: hyperswitch_connectors
// Methods: 6 total (0 public)

impl webhooks::IncomingWebhook for for Authorizedotnet


// File: crates/router/src/core/subscription.rs
// Module: router
// Public functions: 11
// Public structs: 4

use std::str::FromStr;

use api_models::{
    enums as api_enums,
    subscription::{self as subscription_types, CreateSubscriptionResponse, SubscriptionStatus},
};
use common_utils::{ext_traits::ValueExt, id_type::GenerateId, pii};
use diesel_models::subscription::SubscriptionNew;
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    api::ApplicationResponse,
    merchant_context::MerchantContext,
    router_data_v2::flow_common_types::{SubscriptionCreateData, SubscriptionCustomerData},
    router_request_types::{subscriptions as subscription_request_types, ConnectorCustomerData},
    router_response_types::{
        subscriptions as subscription_response_types, ConnectorCustomerResponseData,
        PaymentsResponseData,
    },
};
use masking::Secret;

use super::errors::{self, RouterResponse};
use crate::{
    core::payments as payments_core, routes::SessionState, services, types::api as api_types,
};

pub const SUBSCRIPTION_CONNECTOR_ID: &str = "DefaultSubscriptionConnectorId";
pub const SUBSCRIPTION_PAYMENT_ID: &str = "DefaultSubscriptionPaymentId";

pub async fn create_subscription(
    state: SessionState,
    merchant_context: MerchantContext,
    profile_id: String,
    request: subscription_types::CreateSubscriptionRequest,
) -> RouterResponse<CreateSubscriptionResponse> {
    let store = state.store.clone();
    let db = store.as_ref();
    let id = common_utils::id_type::SubscriptionId::generate();
    let profile_id = common_utils::id_type::ProfileId::from_str(&profile_id).change_context(
        errors::ApiErrorResponse::InvalidDataValue {
            field_name: "X-Profile-Id",
        },
    )?;

    let mut subscription = SubscriptionNew::new(
        id,
        SubscriptionStatus::Created.to_string(),
        None,
        None,
        None,
        None,
        None,
        merchant_context.get_merchant_account().get_id().clone(),
        request.customer_id.clone(),
        None,
        profile_id,
        request.merchant_reference_id,
    );

    subscription.generate_and_set_client_secret();
    let subscription_response = db
        .insert_subscription_entry(subscription)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("subscriptions: unable to insert subscription entry to database")?;

    let response = CreateSubscriptionResponse::new(
        subscription_response.id.clone(),
        subscription_response.merchant_reference_id,
        SubscriptionStatus::from_str(&subscription_response.status)
            .unwrap_or(SubscriptionStatus::Created),
        None,
        subscription_response.profile_id,
        subscription_response.merchant_id,
        subscription_response.client_secret.map(Secret::new),
        request.customer_id,
    );

    Ok(ApplicationResponse::Json(response))
}

pub async fn confirm_subscription(
    state: SessionState,
    merchant_context: MerchantContext,
    profile_id: String,
    request: subscription_types::ConfirmSubscriptionRequest,
    subscription_id: common_utils::id_type::SubscriptionId,
) -> RouterResponse<subscription_types::ConfirmSubscriptionResponse> {
    let profile_id = common_utils::id_type::ProfileId::from_str(&profile_id).change_context(
        errors::ApiErrorResponse::InvalidDataValue {
            field_name: "X-Profile-Id",
        },
    )?;

    let key_manager_state = &(&state).into();
    let merchant_key_store = merchant_context.get_merchant_key_store();

    let profile = state
        .store
        .find_business_profile_by_profile_id(key_manager_state, merchant_key_store, &profile_id)
        .await
        .change_context(errors::ApiErrorResponse::ProfileNotFound {
            id: profile_id.get_string_repr().to_string(),
        })?;

    let customer = state
        .store
        .find_customer_by_customer_id_merchant_id(
            key_manager_state,
            &request.customer_id,
            merchant_context.get_merchant_account().get_id(),
            merchant_key_store,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .change_context(errors::ApiErrorResponse::CustomerNotFound)
        .attach_printable("subscriptions: unable to fetch customer from database")?;

    let handler = SubscriptionHandler::new(state, merchant_context, request, profile);

    let mut subscription_entry = handler
        .find_subscription(subscription_id.get_string_repr().to_string())
        .await?;

    let billing_handler = subscription_entry.get_billing_handler(customer).await?;
    let invoice_handler = subscription_entry.get_invoice_handler().await?;

    let _customer_create_response = billing_handler
        .create_customer_on_connector(&handler.state)
        .await?;

    let subscription_create_response = billing_handler
        .create_subscription_on_connector(&handler.state)
        .await?;

    // let payment_response = invoice_handler.create_cit_payment().await?;

    let invoice_entry = invoice_handler
        .create_invoice_entry(
            &handler.state,
            subscription_entry.profile.get_billing_processor_id()?,
            None,
            billing_handler.request.amount,
            billing_handler.request.currency.to_string(),
            common_enums::connector_enums::InvoiceStatus::InvoiceCreated,
            billing_handler.connector_data.connector_name,
            None,
        )
        .await?;

    // invoice_entry
    //     .create_invoice_record_back_job(&payment_response)
    //     .await?;

    subscription_entry
        .update_subscription_status(
            SubscriptionStatus::from(subscription_create_response.status).to_string(),
        )
        .await?;

    let response = subscription_entry
        .generate_response(&invoice_entry, subscription_create_response.status)?;

    Ok(ApplicationResponse::Json(response))
}

pub struct SubscriptionHandler {
    state: SessionState,
    merchant_context: MerchantContext,
    request: subscription_types::ConfirmSubscriptionRequest,
    profile: hyperswitch_domain_models::business_profile::Profile,
}

impl SubscriptionHandler {
    pub fn new(
        state: SessionState,
        merchant_context: MerchantContext,
        request: subscription_types::ConfirmSubscriptionRequest,
        profile: hyperswitch_domain_models::business_profile::Profile,
    ) -> Self {
        Self {
            state,
            merchant_context,
            request,
            profile,
        }
    }
    pub async fn find_subscription(
        &self,
        subscription_id: String,
    ) -> errors::RouterResult<SubscriptionWithHandler<'_>> {
        let subscription = self
            .state
            .store
            .find_by_merchant_id_subscription_id(
                self.merchant_context.get_merchant_account().get_id(),
                subscription_id.clone(),
            )
            .await
            .change_context(errors::ApiErrorResponse::GenericNotFoundError {
                message: format!("subscription not found for id: {subscription_id}"),
            })?;

        Ok(SubscriptionWithHandler {
            handler: self,
            subscription,
            profile: self.profile.clone(),
        })
    }
}
pub struct SubscriptionWithHandler<'a> {
    handler: &'a SubscriptionHandler,
    subscription: diesel_models::subscription::Subscription,
    profile: hyperswitch_domain_models::business_profile::Profile,
}

impl<'a> SubscriptionWithHandler<'a> {
    fn generate_response(
        &self,
        invoice: &diesel_models::invoice::Invoice,
        // _payment_response: &subscription_types::PaymentResponseData,
        status: subscription_response_types::SubscriptionStatus,
    ) -> errors::RouterResult<subscription_types::ConfirmSubscriptionResponse> {
        Ok(subscription_types::ConfirmSubscriptionResponse {
            id: self.subscription.id.clone(),
            merchant_reference_id: self.subscription.merchant_reference_id.clone(),
            status: SubscriptionStatus::from(status),
            plan_id: None,
            profile_id: self.subscription.profile_id.to_owned(),
            payment: None,
            customer_id: Some(self.subscription.customer_id.clone()),
            price_id: None,
            coupon: None,
            invoice: Some(subscription_types::Invoice {
                id: invoice.id.clone(),
                subscription_id: invoice.subscription_id.clone(),
                merchant_id: invoice.merchant_id.clone(),
                profile_id: invoice.profile_id.clone(),
                merchant_connector_id: invoice.merchant_connector_id.clone(),
                payment_intent_id: invoice.payment_intent_id.clone(),
                payment_method_id: invoice.payment_method_id.clone(),
                customer_id: invoice.customer_id.clone(),
                amount: invoice.amount,
                currency: api_enums::Currency::from_str(invoice.currency.as_str())
                    .change_context(errors::ApiErrorResponse::InvalidDataValue {
                        field_name: "currency",
                    })
                    .attach_printable(format!(
                        "unable to parse currency name {currency:?}",
                        currency = invoice.currency
                    ))?,
                status: invoice.status.clone(),
            }),
        })
    }

    async fn update_subscription_status(&mut self, status: String) -> errors::RouterResult<()> {
        let db = self.handler.state.store.as_ref();
        let updated_subscription = db
            .update_subscription_entry(
                self.handler
                    .merchant_context
                    .get_merchant_account()
                    .get_id(),
                self.subscription.id.get_string_repr().to_string(),
                diesel_models::subscription::SubscriptionUpdate::new(None, Some(status)),
            )
            .await
            .change_context(errors::ApiErrorResponse::SubscriptionError {
                operation: "Subscription Update".to_string(),
            })
            .attach_printable("subscriptions: unable to update subscription entry in database")?;

        self.subscription = updated_subscription;

        Ok(())
    }

    pub async fn get_billing_handler(
        &self,
        customer: hyperswitch_domain_models::customer::Customer,
    ) -> errors::RouterResult<BillingHandler> {
        let mca_id = self.profile.get_billing_processor_id()?;

        let billing_processor_mca = self
            .handler
            .state
            .store
            .find_by_merchant_connector_account_merchant_id_merchant_connector_id(
                &(&self.handler.state).into(),
                self.handler
                    .merchant_context
                    .get_merchant_account()
                    .get_id(),
                &mca_id,
                self.handler.merchant_context.get_merchant_key_store(),
            )
            .await
            .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                id: mca_id.get_string_repr().to_string(),
            })?;

        let connector_name = billing_processor_mca.connector_name.clone();

        let auth_type: hyperswitch_domain_models::router_data::ConnectorAuthType =
            payments_core::helpers::MerchantConnectorAccountType::DbVal(Box::new(
                billing_processor_mca.clone(),
            ))
            .get_connector_account_details()
            .parse_value("ConnectorAuthType")
            .change_context(errors::ApiErrorResponse::InvalidDataFormat {
                field_name: "connector_account_details".to_string(),
                expected_format: "auth_type and api_key".to_string(),
            })?;

        let connector_data = api_types::ConnectorData::get_connector_by_name(
            &self.handler.state.conf.connectors,
            &connector_name,
            api_types::GetToken::Connector,
            Some(billing_processor_mca.get_id()),
        )
        .change_context(errors::ApiErrorResponse::IncorrectConnectorNameGiven)
        .attach_printable(
            "invalid connector name received in billing merchant connector account",
        )?;

        let connector_enum =
            common_enums::connector_enums::Connector::from_str(connector_name.as_str())
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable(format!("unable to parse connector name {connector_name:?}"))?;

        let connector_params =
            hyperswitch_domain_models::connector_endpoints::Connectors::get_connector_params(
                &self.handler.state.conf.connectors,
                connector_enum,
            )
            .change_context(errors::ApiErrorResponse::ConfigNotFound)
            .attach_printable(format!(
                "cannot find connector params for this connector {connector_name} in this flow",
            ))?;

        Ok(BillingHandler {
            subscription: self.subscription.clone(),
            auth_type,
            connector_data,
            connector_params,
            request: self.handler.request.clone(),
            connector_metadata: billing_processor_mca.metadata.clone(),
            customer,
        })
    }

    pub async fn get_invoice_handler(&self) -> errors::RouterResult<InvoiceHandler> {
        Ok(InvoiceHandler {
            subscription: self.subscription.clone(),
        })
    }
}

pub struct BillingHandler {
    subscription: diesel_models::subscription::Subscription,
    auth_type: hyperswitch_domain_models::router_data::ConnectorAuthType,
    connector_data: api_types::ConnectorData,
    connector_params: hyperswitch_domain_models::connector_endpoints::ConnectorParams,
    connector_metadata: Option<pii::SecretSerdeValue>,
    customer: hyperswitch_domain_models::customer::Customer,
    request: subscription_types::ConfirmSubscriptionRequest,
}

pub struct InvoiceHandler {
    subscription: diesel_models::subscription::Subscription,
}

#[allow(clippy::todo)]
impl InvoiceHandler {
    #[allow(clippy::too_many_arguments)]
    pub async fn create_invoice_entry(
        self,
        state: &SessionState,
        merchant_connector_id: common_utils::id_type::MerchantConnectorAccountId,
        payment_intent_id: Option<common_utils::id_type::PaymentId>,
        amount: common_utils::types::MinorUnit,
        currency: String,
        status: common_enums::connector_enums::InvoiceStatus,
        provider_name: common_enums::connector_enums::Connector,
        metadata: Option<pii::SecretSerdeValue>,
    ) -> errors::RouterResult<diesel_models::invoice::Invoice> {
        let invoice_new = diesel_models::invoice::InvoiceNew::new(
            self.subscription.id.to_owned(),
            self.subscription.merchant_id.to_owned(),
            self.subscription.profile_id.to_owned(),
            merchant_connector_id,
            payment_intent_id,
            self.subscription.payment_method_id.clone(),
            self.subscription.customer_id.to_owned(),
            amount,
            currency,
            status,
            provider_name,
            metadata,
        );

        let invoice = state
            .store
            .insert_invoice_entry(invoice_new)
            .await
            .change_context(errors::ApiErrorResponse::SubscriptionError {
                operation: "Subscription Confirm".to_string(),
            })
            .attach_printable("invoices: unable to insert invoice entry to database")?;

        Ok(invoice)
    }

    pub async fn create_cit_payment(
        &self,
    ) -> errors::RouterResult<subscription_types::PaymentResponseData> {
        // Create a CIT payment for the invoice
        todo!("Create a CIT payment for the invoice")
    }

    pub async fn create_invoice_record_back_job(
        &self,
        // _invoice: &subscription_types::Invoice,
        _payment_response: &subscription_types::PaymentResponseData,
    ) -> errors::RouterResult<()> {
        // Create an invoice job entry based on payment status
        todo!("Create an invoice job entry based on payment status")
    }
}

#[allow(clippy::todo)]
impl BillingHandler {
    pub async fn create_customer_on_connector(
        &self,
        state: &SessionState,
    ) -> errors::RouterResult<ConnectorCustomerResponseData> {
        let customer_req = ConnectorCustomerData {
            email: self.customer.email.clone().map(pii::Email::from),
            payment_method_data: self
                .request
                .payment_details
                .payment_method_data
                .payment_method_data
                .clone()
                .map(|pmd| pmd.into()),
            description: None,
            phone: None,
            name: None,
            preprocessing_id: None,
            split_payments: None,
            setup_future_usage: None,
            customer_acceptance: None,
            customer_id: Some(self.subscription.customer_id.to_owned()),
            billing_address: self
                .request
                .billing_address
                .as_ref()
                .and_then(|add| add.address.clone())
                .and_then(|addr| addr.into()),
        };
        let router_data = self.build_router_data(
            state,
            customer_req,
            SubscriptionCustomerData {
                connector_meta_data: self.connector_metadata.clone(),
            },
        )?;
        let connector_integration = self.connector_data.connector.get_connector_integration();

        let response = Box::pin(self.call_connector(
            state,
            router_data,
            "create customer on connector",
            connector_integration,
        ))
        .await?;

        match response {
            Ok(response_data) => match response_data {
                PaymentsResponseData::ConnectorCustomerResponse(customer_response) => {
                    Ok(customer_response)
                }
                _ => Err(errors::ApiErrorResponse::SubscriptionError {
                    operation: "Subscription Customer Create".to_string(),
                }
                .into()),
            },
            Err(err) => Err(errors::ApiErrorResponse::ExternalConnectorError {
                code: err.code,
                message: err.message,
                connector: self.connector_data.connector_name.to_string(),
                status_code: err.status_code,
                reason: err.reason,
            }
            .into()),
        }
    }
    pub async fn create_subscription_on_connector(
        &self,
        state: &SessionState,
    ) -> errors::RouterResult<subscription_response_types::SubscriptionCreateResponse> {
        let subscription_item = subscription_request_types::SubscriptionItem {
            item_price_id: self.request.get_item_price_id().change_context(
                errors::ApiErrorResponse::MissingRequiredField {
                    field_name: "item_price_id",
                },
            )?,
            quantity: Some(1),
        };
        let subscription_req = subscription_request_types::SubscriptionCreateRequest {
            subscription_id: self.subscription.id.to_owned(),
            customer_id: self.subscription.customer_id.to_owned(),
            subscription_items: vec![subscription_item],
            billing_address: self.request.get_billing_address().change_context(
                errors::ApiErrorResponse::MissingRequiredField {
                    field_name: "billing_address",
                },
            )?,
            auto_collection: subscription_request_types::SubscriptionAutoCollection::Off,
            connector_params: self.connector_params.clone(),
        };

        let router_data = self.build_router_data(
            state,
            subscription_req,
            SubscriptionCreateData {
                connector_meta_data: self.connector_metadata.clone(),
            },
        )?;
        let connector_integration = self.connector_data.connector.get_connector_integration();

        let response = self
            .call_connector(
                state,
                router_data,
                "create subscription on connector",
                connector_integration,
            )
            .await?;

        match response {
            Ok(response_data) => Ok(response_data),
            Err(err) => Err(errors::ApiErrorResponse::ExternalConnectorError {
                code: err.code,
                message: err.message,
                connector: self.connector_data.connector_name.to_string(),
                status_code: err.status_code,
                reason: err.reason,
            }
            .into()),
        }
    }

    async fn call_connector<F, ResourceCommonData, Req, Resp>(
        &self,
        state: &SessionState,
        router_data: hyperswitch_domain_models::router_data_v2::RouterDataV2<
            F,
            ResourceCommonData,
            Req,
            Resp,
        >,
        operation_name: &str,
        connector_integration: hyperswitch_interfaces::connector_integration_interface::BoxedConnectorIntegrationInterface<F, ResourceCommonData, Req, Resp>,
    ) -> errors::RouterResult<Result<Resp, hyperswitch_domain_models::router_data::ErrorResponse>>
    where
        F: Clone + std::fmt::Debug + 'static,
        Req: Clone + std::fmt::Debug + 'static,
        Resp: Clone + std::fmt::Debug + 'static,
        ResourceCommonData:
            hyperswitch_interfaces::connector_integration_interface::RouterDataConversion<
                    F,
                    Req,
                    Resp,
                > + Clone
                + 'static,
    {
        let old_router_data = ResourceCommonData::to_old_router_data(router_data).change_context(
            errors::ApiErrorResponse::SubscriptionError {
                operation: { operation_name.to_string() },
            },
        )?;

        let router_resp = services::execute_connector_processing_step(
            state,
            connector_integration,
            &old_router_data,
            payments_core::CallConnectorAction::Trigger,
            None,
            None,
        )
        .await
        .change_context(errors::ApiErrorResponse::SubscriptionError {
            operation: operation_name.to_string(),
        })
        .attach_printable(format!(
            "Failed while in subscription operation: {operation_name}"
        ))?;

        Ok(router_resp.response)
    }

    fn build_router_data<F, ResourceCommonData, Req, Resp>(
        &self,
        state: &SessionState,
        req: Req,
        resource_common_data: ResourceCommonData,
    ) -> errors::RouterResult<
        hyperswitch_domain_models::router_data_v2::RouterDataV2<F, ResourceCommonData, Req, Resp>,
    > {
        Ok(hyperswitch_domain_models::router_data_v2::RouterDataV2 {
            flow: std::marker::PhantomData,
            connector_auth_type: self.auth_type.clone(),
            resource_common_data,
            tenant_id: state.tenant.tenant_id.clone(),
            request: req,
            response: Err(hyperswitch_domain_models::router_data::ErrorResponse::default()),
        })
    }
}


// File: crates/hyperswitch_domain_models/src/cards_info.rs
// Module: hyperswitch_domain_models

use common_utils::errors;
use diesel_models::cards_info;

#[async_trait::async_trait]
pub trait CardsInfoInterface {
    type Error;
    async fn get_card_info(
        &self,
        _card_iin: &str,
    ) -> errors::CustomResult<Option<cards_info::CardInfo>, Self::Error>;
    async fn add_card_info(
        &self,
        data: cards_info::CardInfo,
    ) -> errors::CustomResult<cards_info::CardInfo, Self::Error>;
    async fn update_card_info(
        &self,
        card_iin: String,
        data: cards_info::UpdateCardInfo,
    ) -> errors::CustomResult<cards_info::CardInfo, Self::Error>;
}


// Trait: DomainType
// File: crates/storage_impl/src/kv_router_store.rs
// Module: storage_impl

pub trait DomainType: Debug + Sync + Conversion


// Implementation: impl super::disputes::filters::DisputeFilterAnalytics for for ClickhouseClient
// File: crates/analytics/src/clickhouse.rs
// Module: analytics
// Methods: 0 total (0 public)

impl super::disputes::filters::DisputeFilterAnalytics for for ClickhouseClient


// Struct: CardDetailUpdate
// File: crates/api_models/src/payment_methods.rs
// Module: api_models
// Implementations: 2

pub struct CardDetailUpdate


// Implementation: impl Default for for Memoization
// File: crates/hyperswitch_constraint_graph/src/types.rs
// Module: hyperswitch_constraint_graph
// Methods: 1 total (0 public)

impl Default for for Memoization


// Implementation: impl Serialize for for StrongSecret
// File: crates/masking/src/serde.rs
// Module: masking
// Methods: 1 total (0 public)

impl Serialize for for StrongSecret


// File: crates/router/tests/connectors/tokenex.rs
// Module: router

use hyperswitch_domain_models::payment_method_data::{Card, PaymentMethodData};
use masking::Secret;
use router::types::{self, api, storage::enums};
use test_utils::connector_auth;

use crate::utils::{self, ConnectorActions};

#[derive(Clone, Copy)]
struct TokenexTest;
impl ConnectorActions for TokenexTest {}
impl utils::Connector for TokenexTest {
    fn get_data(&self) -> api::ConnectorData {
        use router::connector::Tokenex;
        utils::construct_connector_data_old(
            Box::new(&Tokenex),
            types::Connector::Tokenex,
            api::GetToken::Connector,
            None,
        )
    }

    fn get_auth_token(&self) -> types::ConnectorAuthType {
        utils::to_connector_auth_type(
            connector_auth::ConnectorAuthentication::new()
                .tokenex
                .expect("Missing connector authentication configuration")
                .into(),
        )
    }

    fn get_name(&self) -> String {
        "tokenex".to_string()
    }
}

static CONNECTOR: TokenexTest = TokenexTest {};

fn get_default_payment_info() -> Option<utils::PaymentInfo> {
    None
}

fn payment_method_details() -> Option<types::PaymentsAuthorizeData> {
    None
}

// Cards Positive Tests
// Creates a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_only_authorize_payment() {
    let response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    assert_eq!(response.status, enums::AttemptStatus::Authorized);
}

// Captures a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_capture_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_capture_payment(payment_method_details(), None, get_default_payment_info())
        .await
        .expect("Capture payment response");
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}

// Partially captures a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_capture_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_capture_payment(
            payment_method_details(),
            Some(types::PaymentsCaptureData {
                amount_to_capture: 50,
                ..utils::PaymentCaptureType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .expect("Capture payment response");
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}

// Synchronizes a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_authorized_payment() {
    let authorize_response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Authorized,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    txn_id.unwrap(),
                ),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("PSync response");
    assert_eq!(response.status, enums::AttemptStatus::Authorized,);
}

// Voids a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_void_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_void_payment(
            payment_method_details(),
            Some(types::PaymentsCancelData {
                connector_transaction_id: String::from(""),
                cancellation_reason: Some("requested_by_customer".to_string()),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("Void payment response");
    assert_eq!(response.status, enums::AttemptStatus::Voided);
}

// Refunds a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_manually_captured_payment() {
    let response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Partially refunds a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_refund_manually_captured_payment() {
    let response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Synchronizes a refund using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_manually_captured_refund() {
    let refund_response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    let response = CONNECTOR
        .rsync_retry_till_status_matches(
            enums::RefundStatus::Success,
            refund_response.response.unwrap().connector_refund_id,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Creates a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_make_payment() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
}

// Synchronizes a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_auto_captured_payment() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    assert_ne!(txn_id, None, "Empty connector transaction id");
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Charged,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    txn_id.unwrap(),
                ),
                capture_method: Some(enums::CaptureMethod::Automatic),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(response.status, enums::AttemptStatus::Charged,);
}

// Refunds a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_auto_captured_payment() {
    let response = CONNECTOR
        .make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Partially refunds a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_refund_succeeded_payment() {
    let refund_response = CONNECTOR
        .make_payment_and_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        refund_response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Creates multiple refunds against a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_succeeded_payment_multiple_times() {
    CONNECTOR
        .make_payment_and_multiple_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await;
}

// Synchronizes a refund using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_refund() {
    let refund_response = CONNECTOR
        .make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
        .await
        .unwrap();
    let response = CONNECTOR
        .rsync_retry_till_status_matches(
            enums::RefundStatus::Success,
            refund_response.response.unwrap().connector_refund_id,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Cards Negative scenarios
// Creates a payment with incorrect CVC.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_cvc() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: PaymentMethodData::Card(Card {
                    card_cvc: Secret::new("12345".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's security code is invalid.".to_string(),
    );
}

// Creates a payment with incorrect expiry month.
#[actix_web::test]
async fn should_fail_payment_for_invalid_exp_month() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: PaymentMethodData::Card(Card {
                    card_exp_month: Secret::new("20".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's expiration month is invalid.".to_string(),
    );
}

// Creates a payment with incorrect expiry year.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_expiry_year() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: PaymentMethodData::Card(Card {
                    card_exp_year: Secret::new("2000".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's expiration year is invalid.".to_string(),
    );
}

// Voids a payment using automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_fail_void_payment_for_auto_capture() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    assert_ne!(txn_id, None, "Empty connector transaction id");
    let void_response = CONNECTOR
        .void_payment(txn_id.unwrap(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        void_response.response.unwrap_err().message,
        "You cannot cancel this PaymentIntent because it has a status of succeeded."
    );
}

// Captures a payment using invalid connector payment id.
#[actix_web::test]
async fn should_fail_capture_for_invalid_payment() {
    let capture_response = CONNECTOR
        .capture_payment("123456789".to_string(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        capture_response.response.unwrap_err().message,
        String::from("No such payment_intent: '123456789'")
    );
}

// Refunds a payment with refund amount higher than payment amount.
#[actix_web::test]
async fn should_fail_for_refund_amount_higher_than_payment_amount() {
    let response = CONNECTOR
        .make_payment_and_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 150,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Refund amount (₹1.50) is greater than charge amount (₹1.00)",
    );
}

// Connector dependent test cases goes here

// [#478]: add unit tests for non 3DS, wallets & webhooks in connector tests


// Function: generate_network_token
// File: crates/router/src/core/payment_methods/network_tokenization.rs
// Module: router

pub fn generate_network_token(
    state: &routes::SessionState,
    payload_bytes: &[u8],
    customer_id: id_type::GlobalCustomerId,
    tokenization_service: &settings::NetworkTokenizationService,
) -> CustomResult<
    (pm_types::GenerateNetworkTokenResponsePayload, String),
    errors::NetworkTokenizationError,
>


// Struct: Airwallex
// File: crates/hyperswitch_connectors/src/connectors/airwallex.rs
// Module: hyperswitch_connectors
// Implementations: 20
// Traits: ConnectorCommon, ConnectorValidation, api::Payment, api::PaymentsPreProcessing, api::PaymentsCompleteAuthorize, api::MandateSetup, api::PaymentToken, api::ConnectorAccessToken, api::PaymentAuthorize, api::PaymentSync, api::PaymentCapture, api::PaymentSession, api::PaymentVoid, api::Refund, api::RefundExecute, api::RefundSync, IncomingWebhook, ConnectorRedirectResponse, ConnectorSpecifications

pub struct Airwallex

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


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

pub struct RefundResponse


// Struct: WebhookResponseData
// File: crates/hyperswitch_connectors/src/connectors/getnet/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct WebhookResponseData


// Implementation: impl ApiEventMetric for for FrmFilterValue
// File: crates/api_models/src/analytics.rs
// Module: api_models
// Methods: 0 total (0 public)

impl ApiEventMetric for for FrmFilterValue


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

impl Fiuu


// File: crates/router/src/core/payment_methods/tokenize.rs
// Module: router
// Public functions: 3
// Public structs: 4

use actix_multipart::form::{bytes::Bytes, text::Text, MultipartForm};
use api_models::{enums as api_enums, payment_methods as payment_methods_api};
use cards::CardNumber;
use common_utils::{
    crypto::Encryptable,
    id_type,
    transformers::{ForeignFrom, ForeignTryFrom},
};
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::router_request_types as domain_request_types;
use masking::{ExposeInterface, Secret};
use router_env::logger;

use crate::{
    core::payment_methods::{
        cards::{add_card_to_hs_locker, create_encrypted_data, tokenize_card_flow},
        network_tokenization, transformers as pm_transformers,
    },
    errors::{self, RouterResult},
    services,
    types::{api, domain, payment_methods as pm_types},
    SessionState,
};

pub mod card_executor;
pub mod payment_method_executor;

pub use card_executor::*;
pub use payment_method_executor::*;
use rdkafka::message::ToBytes;

#[derive(Debug, MultipartForm)]
pub struct CardNetworkTokenizeForm {
    #[multipart(limit = "1MB")]
    pub file: Bytes,
    pub merchant_id: Text<id_type::MerchantId>,
}

pub fn parse_csv(
    merchant_id: &id_type::MerchantId,
    data: &[u8],
) -> csv::Result<Vec<payment_methods_api::CardNetworkTokenizeRequest>> {
    let mut csv_reader = csv::ReaderBuilder::new()
        .has_headers(true)
        .from_reader(data);
    let mut records = Vec::new();
    let mut id_counter = 0;
    for (i, result) in csv_reader
        .deserialize::<domain::CardNetworkTokenizeRecord>()
        .enumerate()
    {
        match result {
            Ok(mut record) => {
                logger::info!("Parsed Record (line {}): {:?}", i + 1, record);
                id_counter += 1;
                record.line_number = Some(id_counter);
                record.merchant_id = Some(merchant_id.clone());
                match payment_methods_api::CardNetworkTokenizeRequest::foreign_try_from(record) {
                    Ok(record) => {
                        records.push(record);
                    }
                    Err(err) => {
                        logger::error!("Error parsing line {}: {}", i + 1, err.to_string());
                    }
                }
            }
            Err(e) => logger::error!("Error parsing line {}: {}", i + 1, e),
        }
    }
    Ok(records)
}

pub fn get_tokenize_card_form_records(
    form: CardNetworkTokenizeForm,
) -> Result<
    (
        id_type::MerchantId,
        Vec<payment_methods_api::CardNetworkTokenizeRequest>,
    ),
    errors::ApiErrorResponse,
> {
    match parse_csv(&form.merchant_id, form.file.data.to_bytes()) {
        Ok(records) => {
            logger::info!("Parsed a total of {} records", records.len());
            Ok((form.merchant_id.0, records))
        }
        Err(e) => {
            logger::error!("Failed to parse CSV: {:?}", e);
            Err(errors::ApiErrorResponse::PreconditionFailed {
                message: e.to_string(),
            })
        }
    }
}

pub async fn tokenize_cards(
    state: &SessionState,
    records: Vec<payment_methods_api::CardNetworkTokenizeRequest>,
    merchant_context: &domain::MerchantContext,
) -> errors::RouterResponse<Vec<payment_methods_api::CardNetworkTokenizeResponse>> {
    use futures::stream::StreamExt;

    // Process all records in parallel
    let responses = futures::stream::iter(records.into_iter())
        .map(|record| async move {
            let tokenize_request = record.data.clone();
            let customer = record.customer.clone();
            Box::pin(tokenize_card_flow(
                state,
                domain::CardNetworkTokenizeRequest::foreign_from(record),
                merchant_context,
            ))
            .await
            .unwrap_or_else(|e| {
                let err = e.current_context();
                payment_methods_api::CardNetworkTokenizeResponse {
                    tokenization_data: Some(tokenize_request),
                    error_code: Some(err.error_code()),
                    error_message: Some(err.error_message()),
                    card_tokenized: false,
                    payment_method_response: None,
                    customer: Some(customer),
                }
            })
        })
        .buffer_unordered(10)
        .collect()
        .await;

    // Return the final response
    Ok(services::ApplicationResponse::Json(responses))
}

// Data types
type NetworkTokenizationResponse = (pm_types::CardNetworkTokenResponsePayload, Option<String>);

pub struct StoreLockerResponse {
    pub store_card_resp: pm_transformers::StoreCardRespPayload,
    pub store_token_resp: pm_transformers::StoreCardRespPayload,
}

// Builder
pub struct NetworkTokenizationBuilder<'a, S: State> {
    /// Current state
    state: std::marker::PhantomData<S>,

    /// Customer details
    pub customer: Option<&'a api::CustomerDetails>,

    /// Card details
    pub card: Option<domain::CardDetail>,

    /// CVC
    pub card_cvc: Option<Secret<String>>,

    /// Network token details
    pub network_token: Option<&'a pm_types::CardNetworkTokenResponsePayload>,

    /// Stored card details
    pub stored_card: Option<&'a pm_transformers::StoreCardRespPayload>,

    /// Stored token details
    pub stored_token: Option<&'a pm_transformers::StoreCardRespPayload>,

    /// Payment method response
    pub payment_method_response: Option<api::PaymentMethodResponse>,

    /// Card network tokenization status
    pub card_tokenized: bool,

    /// Error code
    pub error_code: Option<&'a String>,

    /// Error message
    pub error_message: Option<&'a String>,
}

// Async executor
pub struct CardNetworkTokenizeExecutor<'a, D> {
    pub state: &'a SessionState,
    pub merchant_account: &'a domain::MerchantAccount,
    key_store: &'a domain::MerchantKeyStore,
    data: &'a D,
    customer: &'a domain_request_types::CustomerDetails,
}

// State machine
pub trait State {}
pub trait TransitionTo<S: State> {}

// Trait for network tokenization
#[async_trait::async_trait]
pub trait NetworkTokenizationProcess<'a, D> {
    fn new(
        state: &'a SessionState,
        merchant_context: &'a domain::MerchantContext,
        data: &'a D,
        customer: &'a domain_request_types::CustomerDetails,
    ) -> Self;
    async fn encrypt_card(
        &self,
        card_details: &domain::CardDetail,
        saved_to_locker: bool,
    ) -> RouterResult<Encryptable<Secret<serde_json::Value>>>;
    async fn encrypt_network_token(
        &self,
        network_token_details: &NetworkTokenizationResponse,
        card_details: &domain::CardDetail,
        saved_to_locker: bool,
    ) -> RouterResult<Encryptable<Secret<serde_json::Value>>>;
    async fn fetch_bin_details_and_validate_card_network(
        &self,
        card_number: CardNumber,
        card_issuer: Option<&String>,
        card_network: Option<&api_enums::CardNetwork>,
        card_type: Option<&api_models::payment_methods::CardType>,
        card_issuing_country: Option<&String>,
    ) -> RouterResult<Option<diesel_models::CardInfo>>;
    fn validate_card_network(
        &self,
        optional_card_network: Option<&api_enums::CardNetwork>,
    ) -> RouterResult<()>;
    async fn tokenize_card(
        &self,
        customer_id: &id_type::CustomerId,
        card: &domain::CardDetail,
        optional_cvc: Option<Secret<String>>,
    ) -> RouterResult<NetworkTokenizationResponse>;
    async fn store_network_token_in_locker(
        &self,
        network_token: &NetworkTokenizationResponse,
        customer_id: &id_type::CustomerId,
        card_holder_name: Option<Secret<String>>,
        nick_name: Option<Secret<String>>,
    ) -> RouterResult<pm_transformers::StoreCardRespPayload>;
}

// Generic implementation
#[async_trait::async_trait]
impl<'a, D> NetworkTokenizationProcess<'a, D> for CardNetworkTokenizeExecutor<'a, D>
where
    D: Send + Sync + 'static,
{
    fn new(
        state: &'a SessionState,
        merchant_context: &'a domain::MerchantContext,
        data: &'a D,
        customer: &'a domain_request_types::CustomerDetails,
    ) -> Self {
        Self {
            data,
            customer,
            state,
            merchant_account: merchant_context.get_merchant_account(),
            key_store: merchant_context.get_merchant_key_store(),
        }
    }
    async fn encrypt_card(
        &self,
        card_details: &domain::CardDetail,
        saved_to_locker: bool,
    ) -> RouterResult<Encryptable<Secret<serde_json::Value>>> {
        let pm_data = api::PaymentMethodsData::Card(api::CardDetailsPaymentMethod {
            last4_digits: Some(card_details.card_number.get_last4()),
            expiry_month: Some(card_details.card_exp_month.clone()),
            expiry_year: Some(card_details.card_exp_year.clone()),
            card_isin: Some(card_details.card_number.get_card_isin()),
            nick_name: card_details.nick_name.clone(),
            card_holder_name: card_details.card_holder_name.clone(),
            issuer_country: card_details.card_issuing_country.clone(),
            card_issuer: card_details.card_issuer.clone(),
            card_network: card_details.card_network.clone(),
            card_type: card_details.card_type.clone(),
            saved_to_locker,
            co_badged_card_data: card_details
                .co_badged_card_data
                .as_ref()
                .map(|data| data.into()),
        });

        create_encrypted_data(&self.state.into(), self.key_store, pm_data)
            .await
            .inspect_err(|err| logger::info!("Error encrypting payment method data: {:?}", err))
            .change_context(errors::ApiErrorResponse::InternalServerError)
    }
    async fn encrypt_network_token(
        &self,
        network_token_details: &NetworkTokenizationResponse,
        card_details: &domain::CardDetail,
        saved_to_locker: bool,
    ) -> RouterResult<Encryptable<Secret<serde_json::Value>>> {
        let network_token = &network_token_details.0;
        let token_data = api::PaymentMethodsData::Card(api::CardDetailsPaymentMethod {
            last4_digits: Some(network_token.token_last_four.clone()),
            expiry_month: Some(network_token.token_expiry_month.clone()),
            expiry_year: Some(network_token.token_expiry_year.clone()),
            card_isin: Some(network_token.token_isin.clone()),
            nick_name: card_details.nick_name.clone(),
            card_holder_name: card_details.card_holder_name.clone(),
            issuer_country: card_details.card_issuing_country.clone(),
            card_issuer: card_details.card_issuer.clone(),
            card_network: card_details.card_network.clone(),
            card_type: card_details.card_type.clone(),
            saved_to_locker,
            co_badged_card_data: None,
        });
        create_encrypted_data(&self.state.into(), self.key_store, token_data)
            .await
            .inspect_err(|err| logger::info!("Error encrypting network token data: {:?}", err))
            .change_context(errors::ApiErrorResponse::InternalServerError)
    }
    async fn fetch_bin_details_and_validate_card_network(
        &self,
        card_number: CardNumber,
        card_issuer: Option<&String>,
        card_network: Option<&api_enums::CardNetwork>,
        card_type: Option<&api_models::payment_methods::CardType>,
        card_issuing_country: Option<&String>,
    ) -> RouterResult<Option<diesel_models::CardInfo>> {
        let db = &*self.state.store;
        if card_issuer.is_some()
            && card_network.is_some()
            && card_type.is_some()
            && card_issuing_country.is_some()
        {
            self.validate_card_network(card_network)?;
            return Ok(None);
        }

        db.get_card_info(&card_number.get_card_isin())
            .await
            .attach_printable("Failed to perform BIN lookup")
            .change_context(errors::ApiErrorResponse::InternalServerError)?
            .map(|card_info| {
                self.validate_card_network(card_info.card_network.as_ref())?;
                Ok(card_info)
            })
            .transpose()
    }
    async fn tokenize_card(
        &self,
        customer_id: &id_type::CustomerId,
        card: &domain::CardDetail,
        optional_cvc: Option<Secret<String>>,
    ) -> RouterResult<NetworkTokenizationResponse> {
        network_tokenization::make_card_network_tokenization_request(
            self.state,
            card,
            optional_cvc,
            customer_id,
        )
        .await
        .map_err(|err| {
            logger::error!("Failed to tokenize card with the network: {:?}", err);
            report!(errors::ApiErrorResponse::InternalServerError)
        })
    }
    fn validate_card_network(
        &self,
        optional_card_network: Option<&api_enums::CardNetwork>,
    ) -> RouterResult<()> {
        optional_card_network.map_or(
            Err(report!(errors::ApiErrorResponse::NotSupported {
                message: "Unknown card network".to_string()
            })),
            |card_network| {
                if self
                    .state
                    .conf
                    .network_tokenization_supported_card_networks
                    .card_networks
                    .contains(card_network)
                {
                    Ok(())
                } else {
                    Err(report!(errors::ApiErrorResponse::NotSupported {
                        message: format!(
                            "Network tokenization for {card_network} is not supported",
                        )
                    }))
                }
            },
        )
    }
    async fn store_network_token_in_locker(
        &self,
        network_token: &NetworkTokenizationResponse,
        customer_id: &id_type::CustomerId,
        card_holder_name: Option<Secret<String>>,
        nick_name: Option<Secret<String>>,
    ) -> RouterResult<pm_transformers::StoreCardRespPayload> {
        let network_token = &network_token.0;
        let merchant_id = self.merchant_account.get_id();
        let locker_req =
            pm_transformers::StoreLockerReq::LockerCard(pm_transformers::StoreCardReq {
                merchant_id: merchant_id.clone(),
                merchant_customer_id: customer_id.clone(),
                card: payment_methods_api::Card {
                    card_number: network_token.token.clone(),
                    card_exp_month: network_token.token_expiry_month.clone(),
                    card_exp_year: network_token.token_expiry_year.clone(),
                    card_brand: Some(network_token.card_brand.to_string()),
                    card_isin: Some(network_token.token_isin.clone()),
                    name_on_card: card_holder_name,
                    nick_name: nick_name.map(|nick_name| nick_name.expose()),
                },
                requestor_card_reference: None,
                ttl: self.state.conf.locker.ttl_for_storage_in_secs,
            });

        let stored_resp = add_card_to_hs_locker(
            self.state,
            &locker_req,
            customer_id,
            api_enums::LockerChoice::HyperswitchCardVault,
        )
        .await
        .inspect_err(|err| logger::info!("Error adding card in locker: {:?}", err))
        .change_context(errors::ApiErrorResponse::InternalServerError)?;

        Ok(stored_resp)
    }
}


// Struct: PayuErrorResponse
// File: crates/hyperswitch_connectors/src/connectors/payu/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PayuErrorResponse


// Struct: MerchantAccountUpdate
// File: crates/api_models/src/admin.rs
// Module: api_models
// Implementations: 2

pub struct MerchantAccountUpdate


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

pub struct ChargebeeWebhookBody