    // If not passed in the request, use the default value from constants
    let expires_in = request
        .expires_in
        .unwrap_or(consts::DEFAULT_PAYMENT_METHOD_SESSION_EXPIRY)
        .into();

    let expires_at = common_utils::date_time::now().saturating_add(Duration::seconds(expires_in));

    let client_secret = payment_helpers::create_client_secret(
        &state,
        merchant_context.get_merchant_account().get_id(),
        util_types::authentication::ResourceId::PaymentMethodSession(
            payment_methods_session_id.clone(),
        ),
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Unable to create client secret")?;

    let payment_method_session_domain_model =
        hyperswitch_domain_models::payment_methods::PaymentMethodSession {
            id: payment_methods_session_id,
            customer_id: request.customer_id,
            billing,
            psp_tokenization: request.psp_tokenization,
            network_tokenization: request.network_tokenization,
            tokenization_data: request.tokenization_data,
            expires_at,
            return_url: request.return_url,
            associated_payment_methods: None,
            associated_payment: None,
            associated_token_id: None,
        };

    db.insert_payment_methods_session(
        key_manager_state,
        merchant_context.get_merchant_key_store(),
        payment_method_session_domain_model.clone(),
        expires_in,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to insert payment methods session in db")?;

    let response = transformers::generate_payment_method_session_response(
        payment_method_session_domain_model,
        client_secret.secret,
        None,
        None,
    );

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

#[cfg(feature = "v2")]
pub async fn payment_methods_session_update(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    payment_method_session_id: id_type::GlobalPaymentMethodSessionId,
    request: payment_methods::PaymentMethodsSessionUpdateRequest,
) -> RouterResponse<payment_methods::PaymentMethodSessionResponse> {
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let existing_payment_method_session_state = db
        .get_payment_methods_session(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            &payment_method_session_id,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
            message: "payment methods session does not exist or has expired".to_string(),
        })
        .attach_printable("Failed to retrieve payment methods session from db")?;

    let encrypted_data = request
        .encrypt_data(key_manager_state, merchant_context.get_merchant_key_store())
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to encrypt payment methods session data")?;

    let billing = encrypted_data
        .billing
        .as_ref()
        .map(|data| {
            data.clone()
                .deserialize_inner_value(|value| value.parse_value("Address"))
        })
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to decode billing address")?;

    let payment_method_session_domain_model =
        hyperswitch_domain_models::payment_methods::PaymentMethodsSessionUpdateEnum::GeneralUpdate{
            billing: Box::new(billing),
            psp_tokenization: request.psp_tokenization,
            network_tokenization: request.network_tokenization,
            tokenization_data: request.tokenization_data,
        };

    let update_state_change = db
        .update_payment_method_session(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            &payment_method_session_id,
            payment_method_session_domain_model,
            existing_payment_method_session_state.clone(),
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to update payment methods session in db")?;

    let response = transformers::generate_payment_method_session_response(
        update_state_change,
        Secret::new("CLIENT_SECRET_REDACTED".to_string()),
        None, // TODO: send associated payments response based on the expandable param
        None,
    );

    Ok(services::ApplicationResponse::Json(response))
}
#[cfg(feature = "v2")]
pub async fn payment_methods_session_retrieve(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    payment_method_session_id: id_type::GlobalPaymentMethodSessionId,
) -> RouterResponse<payment_methods::PaymentMethodSessionResponse> {
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let payment_method_session_domain_model = db
        .get_payment_methods_session(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            &payment_method_session_id,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
            message: "payment methods session does not exist or has expired".to_string(),
        })
        .attach_printable("Failed to retrieve payment methods session from db")?;

    let response = transformers::generate_payment_method_session_response(
        payment_method_session_domain_model,
        Secret::new("CLIENT_SECRET_REDACTED".to_string()),
        None, // TODO: send associated payments response based on the expandable param
        None,
    );

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

#[cfg(feature = "v2")]
pub async fn payment_methods_session_update_payment_method(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile: domain::Profile,
    payment_method_session_id: id_type::GlobalPaymentMethodSessionId,
    request: payment_methods::PaymentMethodSessionUpdateSavedPaymentMethod,
) -> RouterResponse<payment_methods::PaymentMethodResponse> {
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    // Validate if the session still exists
    db.get_payment_methods_session(
        key_manager_state,
        merchant_context.get_merchant_key_store(),
        &payment_method_session_id,
    )
    .await
    .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
        message: "payment methods session does not exist or has expired".to_string(),
    })
    .attach_printable("Failed to retrieve payment methods session from db")?;

    let payment_method_update_request = request.payment_method_update_request;

    let updated_payment_method = update_payment_method_core(
        &state,
        &merchant_context,
        &profile,
        payment_method_update_request,
        &request.payment_method_id,
    )
    .await
    .attach_printable("Failed to update saved payment method")?;

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

#[cfg(feature = "v2")]
pub async fn payment_methods_session_delete_payment_method(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile: domain::Profile,
    pm_id: id_type::GlobalPaymentMethodId,
    payment_method_session_id: id_type::GlobalPaymentMethodSessionId,
) -> RouterResponse<api::PaymentMethodDeleteResponse> {
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    // Validate if the session still exists
    db.get_payment_methods_session(
        key_manager_state,
        merchant_context.get_merchant_key_store(),
        &payment_method_session_id,
    )
    .await
    .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
        message: "payment methods session does not exist or has expired".to_string(),
    })
    .attach_printable("Failed to retrieve payment methods session from db")?;

    let response = delete_payment_method_core(&state, pm_id, &merchant_context, &profile)
        .await
        .attach_printable("Failed to delete saved payment method")?;

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

#[cfg(feature = "v2")]
fn construct_zero_auth_payments_request(
    confirm_request: &payment_methods::PaymentMethodSessionConfirmRequest,
    payment_method_session: &hyperswitch_domain_models::payment_methods::PaymentMethodSession,
    payment_method: &payment_methods::PaymentMethodResponse,
) -> RouterResult<api_models::payments::PaymentsRequest> {
    use api_models::payments;

    Ok(payments::PaymentsRequest {
        amount_details: payments::AmountDetails::new_for_zero_auth_payment(
            common_enums::Currency::USD,
        ),
        payment_method_data: confirm_request.payment_method_data.clone(),
        payment_method_type: confirm_request.payment_method_type,
        payment_method_subtype: confirm_request.payment_method_subtype,
        customer_id: Some(payment_method_session.customer_id.clone()),
        customer_present: Some(enums::PresenceOfCustomerDuringPayment::Present),
        setup_future_usage: Some(common_enums::FutureUsage::OffSession),
        payment_method_id: Some(payment_method.id.clone()),
        merchant_reference_id: None,
        routing_algorithm_id: None,
        capture_method: None,
        authentication_type: None,
        // We have already passed payment method billing address
        billing: None,
        shipping: None,
        description: None,
        return_url: payment_method_session.return_url.clone(),
        apply_mit_exemption: None,
        statement_descriptor: None,
        order_details: None,
        allowed_payment_method_types: None,
        metadata: None,
        connector_metadata: None,
        feature_metadata: None,
        payment_link_enabled: None,
        payment_link_config: None,
        request_incremental_authorization: None,
        session_expiry: None,
        frm_metadata: None,
        request_external_three_ds_authentication: None,
        customer_acceptance: None,
        browser_info: None,
        force_3ds_challenge: None,
        is_iframe_redirection_enabled: None,
        merchant_connector_details: None,
        return_raw_connector_response: None,
        enable_partial_authorization: None,
    })
}

#[cfg(feature = "v2")]
async fn create_zero_auth_payment(
    state: SessionState,
    req_state: routes::app::ReqState,
    merchant_context: domain::MerchantContext,
    profile: domain::Profile,
    request: api_models::payments::PaymentsRequest,
) -> RouterResult<api_models::payments::PaymentsResponse> {
    let response = Box::pin(payments_core::payments_create_and_confirm_intent(
        state,
        req_state,
        merchant_context,
        profile,
        request,
        hyperswitch_domain_models::payments::HeaderPayload::default(),
    ))
    .await?;

    logger::info!(associated_payments_response=?response);

    response
        .get_json_body()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unexpected response from payments core")
}

#[cfg(feature = "v2")]
pub async fn payment_methods_session_confirm(
    state: SessionState,
    req_state: routes::app::ReqState,
    merchant_context: domain::MerchantContext,
    profile: domain::Profile,
    payment_method_session_id: id_type::GlobalPaymentMethodSessionId,
    request: payment_methods::PaymentMethodSessionConfirmRequest,
) -> RouterResponse<payment_methods::PaymentMethodSessionResponse> {
    let db: &dyn StorageInterface = state.store.as_ref();
    let key_manager_state = &(&state).into();

    // Validate if the session still exists
    let payment_method_session = db
        .get_payment_methods_session(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            &payment_method_session_id,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
            message: "payment methods session does not exist or has expired".to_string(),
        })
        .attach_printable("Failed to retrieve payment methods session from db")?;

    let payment_method_session_billing = payment_method_session
        .billing
        .clone()
        .map(|billing| billing.into_inner())
        .map(From::from);

    // Unify the billing address that we receive from the session and from the confirm request
    let unified_billing_address = request
        .payment_method_data
        .billing
        .clone()
        .map(|payment_method_billing| {
            payment_method_billing.unify_address(payment_method_session_billing.as_ref())
        })
        .or_else(|| payment_method_session_billing.clone());

    let customer_id = payment_method_session.customer_id.clone();

    let create_payment_method_request = get_payment_method_create_request(
        request
            .payment_method_data
            .payment_method_data
            .as_ref()
            .get_required_value("payment_method_data")?,
        request.payment_method_type,
        request.payment_method_subtype,
        customer_id.clone(),
        unified_billing_address.as_ref(),
        Some(&payment_method_session),
    )
    .attach_printable("Failed to create payment method request")?;

    let (payment_method_response, payment_method) = Box::pin(create_payment_method_core(
        &state,
        &req_state,
        create_payment_method_request.clone(),
        &merchant_context,
        &profile,
    ))
    .await?;

    let parent_payment_method_token = generate_id(consts::ID_LENGTH, "token");

    let token_data = get_pm_list_token_data(request.payment_method_type, &payment_method)?;

    let intent_fulfillment_time = common_utils::consts::DEFAULT_INTENT_FULFILLMENT_TIME;

    // insert the token data into redis
    if let Some(token_data) = token_data {
        pm_routes::ParentPaymentMethodToken::create_key_for_token((
            &parent_payment_method_token,
            request.payment_method_type,
        ))
        .insert(intent_fulfillment_time, token_data, &state)
        .await?;
    };

    let update_payment_method_session = hyperswitch_domain_models::payment_methods::PaymentMethodsSessionUpdateEnum::UpdateAssociatedPaymentMethods {
        associated_payment_methods:  Some(vec![parent_payment_method_token.clone()])
    };

    vault::insert_cvc_using_payment_token(
        &state,
        &parent_payment_method_token,
        create_payment_method_request.payment_method_data.clone(),
        request.payment_method_type,
        intent_fulfillment_time,
        merchant_context.get_merchant_key_store().key.get_inner(),
    )
    .await?;

    let payment_method_session = db
        .update_payment_method_session(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            &payment_method_session_id,
            update_payment_method_session,
            payment_method_session,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
            message: "payment methods session does not exist or has expired".to_string(),
        })
        .attach_printable("Failed to update payment methods session from db")?;

    let payments_response = match &payment_method_session.psp_tokenization {
        Some(common_types::payment_methods::PspTokenization {
            tokenization_type: common_enums::TokenizationType::MultiUse,
            ..
        }) => {
            let zero_auth_request = construct_zero_auth_payments_request(
                &request,
                &payment_method_session,
                &payment_method_response,
            )?;
            let payments_response = Box::pin(create_zero_auth_payment(
                state.clone(),
                req_state,
                merchant_context.clone(),
                profile.clone(),
                zero_auth_request,
            ))
            .await?;

            Some(payments_response)
        }
        Some(common_types::payment_methods::PspTokenization {
            tokenization_type: common_enums::TokenizationType::SingleUse,
            ..
        }) => {
            Box::pin(create_single_use_tokenization_flow(
                state.clone(),
                req_state.clone(),
                merchant_context.clone(),
                profile.clone(),
                &create_payment_method_request.clone(),
                &payment_method_response,
                &payment_method_session,
            ))
            .await?;
            None
        }
        None => None,
    };

    let tokenization_response = match payment_method_session.tokenization_data.clone() {
        Some(tokenization_data) => {
            let tokenization_response = tokenization_core::create_vault_token_core(
                state.clone(),
                &merchant_context.get_merchant_account().clone(),
                &merchant_context.get_merchant_key_store().clone(),
                api_models::tokenization::GenericTokenizationRequest {
                    customer_id: customer_id.clone(),
                    token_request: tokenization_data,
                },
            )
            .await?;
            let token = match tokenization_response {
                services::ApplicationResponse::Json(response) => Some(response),
                _ => None,
            };
            Some(token)
        }
        None => None,
    };

    logger::debug!(?tokenization_response, "Tokenization response");

    //TODO: update the payment method session with the payment id and payment method id
    let payment_method_session_response = transformers::generate_payment_method_session_response(
        payment_method_session,
        Secret::new("CLIENT_SECRET_REDACTED".to_string()),
        payments_response,
        (tokenization_response.flatten()),
    );

    Ok(services::ApplicationResponse::Json(
        payment_method_session_response,
    ))
}

#[cfg(feature = "v2")]
impl pm_types::SavedPMLPaymentsInfo {
    pub async fn form_payments_info(
        payment_intent: PaymentIntent,
        merchant_context: &domain::MerchantContext,
        profile: domain::Profile,
        db: &dyn StorageInterface,
        key_manager_state: &util_types::keymanager::KeyManagerState,
    ) -> RouterResult<Self> {
        let collect_cvv_during_payment = profile.should_collect_cvv_during_payment;

        let off_session_payment_flag = matches!(
            payment_intent.setup_future_usage,
            common_enums::FutureUsage::OffSession
        );

        let is_connector_agnostic_mit_enabled =
            profile.is_connector_agnostic_mit_enabled.unwrap_or(false);

        Ok(Self {
            payment_intent,
            profile,
            collect_cvv_during_payment,
            off_session_payment_flag,
            is_connector_agnostic_mit_enabled,
        })
    }

    pub async fn perform_payment_ops(
        &self,
        state: &SessionState,
        parent_payment_method_token: Option<String>,
        pma: &api::CustomerPaymentMethodResponseItem,
        pm_list_context: PaymentMethodListContext,
    ) -> RouterResult<()> {
        let token = parent_payment_method_token
            .as_ref()
            .get_required_value("parent_payment_method_token")?;
        let token_data = pm_list_context
            .get_token_data()
            .get_required_value("PaymentTokenData")?;

        let intent_fulfillment_time = self
            .profile
            .get_order_fulfillment_time()
            .unwrap_or(common_utils::consts::DEFAULT_INTENT_FULFILLMENT_TIME);

        pm_routes::ParentPaymentMethodToken::create_key_for_token((token, pma.payment_method_type))
            .insert(intent_fulfillment_time, token_data, state)
            .await?;

        Ok(())
    }
}

#[cfg(feature = "v2")]
#[allow(clippy::too_many_arguments)]
async fn create_single_use_tokenization_flow(
    state: SessionState,
    req_state: routes::app::ReqState,
    merchant_context: domain::MerchantContext,
    profile: domain::Profile,
    payment_method_create_request: &payment_methods::PaymentMethodCreate,
    payment_method: &api::PaymentMethodResponse,
    payment_method_session: &domain::payment_methods::PaymentMethodSession,
) -> RouterResult<()> {
    let customer_id = payment_method_create_request.customer_id.to_owned();
    let connector_id = payment_method_create_request
        .get_tokenize_connector_id()
        .change_context(errors::ApiErrorResponse::MissingRequiredField {
            field_name: "psp_tokenization.connector_id",
        })
        .attach_printable("Failed to get tokenize connector id")?;

    let db = &state.store;

    let merchant_connector_account_details = db
        .find_merchant_connector_account_by_id(
            &(&state).into(),
            &connector_id,
            merchant_context.get_merchant_key_store(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
            id: connector_id.get_string_repr().to_owned(),
        })
        .attach_printable("error while fetching merchant_connector_account from connector_id")?;
    let auth_type = merchant_connector_account_details
        .get_connector_account_details()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed while parsing value for ConnectorAuthType")?;

    let payment_method_data_request = types::PaymentMethodTokenizationData {
        payment_method_data: domain::PaymentMethodData::try_from(
            payment_method_create_request.payment_method_data.clone(),
        )
        .change_context(errors::ApiErrorResponse::MissingRequiredField {
            field_name: "card_cvc",
        })
        .attach_printable(
            "Failed to convert type from Payment Method Create Data to Payment Method Data",
        )?,
        browser_info: None,
        currency: api_models::enums::Currency::default(),
        amount: None,
        split_payments: None,
        mandate_id: None,
        setup_future_usage: None,
        customer_acceptance: None,
        setup_mandate_details: None,
    };

    let payment_method_session_address = types::PaymentAddress::new(
        None,
        payment_method_session
            .billing
            .clone()
            .map(|address| address.into_inner()),
        None,
        None,
    );

    let mut router_data =
        types::RouterData::<api::PaymentMethodToken, _, types::PaymentsResponseData> {
            flow: std::marker::PhantomData,
            merchant_id: merchant_context.get_merchant_account().get_id().clone(),
            customer_id: None,
            connector_customer: None,
            connector: merchant_connector_account_details
                .connector_name
                .to_string(),
            payment_id: consts::IRRELEVANT_PAYMENT_INTENT_ID.to_string(), //Static
            attempt_id: consts::IRRELEVANT_PAYMENT_ATTEMPT_ID.to_string(), //Static
            tenant_id: state.tenant.tenant_id.clone(),
            status: common_enums::enums::AttemptStatus::default(),
            payment_method: common_enums::enums::PaymentMethod::Card,
            connector_auth_type: auth_type,
            description: None,
            address: payment_method_session_address,
            auth_type: common_enums::enums::AuthenticationType::default(),
            connector_meta_data: None,
            connector_wallets_details: None,
            amount_captured: None,
            access_token: None,
            session_token: None,
            reference_id: None,
            payment_method_token: None,
            recurring_mandate_payment_data: None,
            preprocessing_id: None,
            payment_method_balance: None,
            connector_api_version: None,
            request: payment_method_data_request.clone(),
            response: Err(hyperswitch_domain_models::router_data::ErrorResponse::default()),
            connector_request_reference_id: payment_method_session.id.get_string_repr().to_string(),
            #[cfg(feature = "payouts")]
            payout_method_data: None,
            #[cfg(feature = "payouts")]
            quote_id: None,
            test_mode: None,
            connector_http_status_code: None,
            external_latency: None,
            apple_pay_flow: None,
            frm_metadata: None,
            dispute_id: None,
            refund_id: None,
            connector_response: None,
            payment_method_status: None,
            minor_amount_captured: None,
            integrity_check: Ok(()),
            additional_merchant_data: None,
            header_payload: None,
            connector_mandate_request_reference_id: None,
            authentication_id: None,
            psd2_sca_exemption_type: None,
            raw_connector_response: None,
            is_payment_id_from_merchant: None,
            l2_l3_data: None,
            minor_amount_capturable: None,
        };

    let payment_method_token_response = Box::pin(tokenization::add_token_for_payment_method(
        &mut router_data,
        payment_method_data_request.clone(),
        state.clone(),
        &merchant_connector_account_details.clone(),
    ))
    .await?;

    let token_response = payment_method_token_response.token.map_err(|err| {
        errors::ApiErrorResponse::ExternalConnectorError {
            code: err.code,
            message: err.message,
            connector: (merchant_connector_account_details.clone())
                .connector_name
                .to_string(),
            status_code: err.status_code,
            reason: err.reason,
        }
    })?;

    let value = domain::SingleUsePaymentMethodToken::get_single_use_token_from_payment_method_token(
        token_response.clone().into(),
        connector_id.clone(),
    );

    let key = domain::SingleUseTokenKey::store_key(&payment_method.id);

    add_single_use_token_to_store(&state, key, value)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to store single use token")?;

    Ok(())
}

#[cfg(feature = "v2")]
async fn add_single_use_token_to_store(
    state: &SessionState,
    key: domain::SingleUseTokenKey,
    value: domain::SingleUsePaymentMethodToken,
) -> CustomResult<(), errors::StorageError> {
    let redis_connection = state
        .store
        .get_redis_conn()
        .map_err(Into::<errors::StorageError>::into)?;

    redis_connection
        .serialize_and_set_key_with_expiry(
            &domain::SingleUseTokenKey::get_store_key(&key).into(),
            value,
            consts::DEFAULT_PAYMENT_METHOD_STORE_TTL,
        )
        .await
        .change_context(errors::StorageError::KVError)
        .attach_printable("Failed to insert payment method token to redis")?;
    Ok(())
}

#[cfg(feature = "v2")]
async fn get_single_use_token_from_store(
    state: &SessionState,
    key: domain::SingleUseTokenKey,
) -> CustomResult<Option<domain::SingleUsePaymentMethodToken>, errors::StorageError> {
    let redis_connection = state
        .store
        .get_redis_conn()
        .map_err(Into::<errors::StorageError>::into)?;

    redis_connection
        .get_and_deserialize_key::<Option<domain::SingleUsePaymentMethodToken>>(
            &domain::SingleUseTokenKey::get_store_key(&key).into(),
            "SingleUsePaymentMethodToken",
        )
        .await
        .change_context(errors::StorageError::KVError)
        .attach_printable("Failed to get payment method token from redis")
}


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

pub struct RegularTransactionBody


// Struct: BillingAddress
// File: crates/hyperswitch_connectors/src/connectors/worldline/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BillingAddress


// Trait: RoutingEq
// File: crates/router/src/core/payments/routing/utils.rs
// Module: router

pub trait RoutingEq<T>


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

impl Payment for for Threedsecureio


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

impl ConnectorSpecifications for for Worldpayxml


// Struct: ThreeDsMetaData
// File: crates/hyperswitch_domain_models/src/router_request_types/unified_authentication_service.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct ThreeDsMetaData


// Struct: GenericLinkUpdateInternal
// File: crates/diesel_models/src/generic_link.rs
// Module: diesel_models
// Implementations: 0

pub struct GenericLinkUpdateInternal


// File: crates/router/src/services/kafka.rs
// Module: router
// Public functions: 17
// Public structs: 2

use std::{collections::HashMap, sync::Arc};

use common_utils::errors::CustomResult;
use error_stack::{report, ResultExt};
use events::{EventsError, Message, MessagingInterface};
use num_traits::ToPrimitive;
use rdkafka::{
    config::FromClientConfig,
    message::{Header, OwnedHeaders},
    producer::{BaseRecord, DefaultProducerContext, Producer, ThreadedProducer},
};
use serde_json::Value;
use storage_impl::config::TenantConfig;
#[cfg(feature = "payouts")]
pub mod payout;
use diesel_models::fraud_check::FraudCheck;

use crate::{events::EventType, services::kafka::fraud_check_event::KafkaFraudCheckEvent};
mod authentication;
mod authentication_event;
mod dispute;
mod dispute_event;
mod fraud_check;
mod fraud_check_event;
mod payment_attempt;
mod payment_attempt_event;
mod payment_intent;
mod payment_intent_event;
mod refund;
mod refund_event;
pub mod revenue_recovery;
use diesel_models::{authentication::Authentication, refund::Refund};
use hyperswitch_domain_models::payments::{payment_attempt::PaymentAttempt, PaymentIntent};
use serde::Serialize;
use time::{OffsetDateTime, PrimitiveDateTime};

#[cfg(feature = "payouts")]
use self::payout::KafkaPayout;
use self::{
    authentication::KafkaAuthentication, authentication_event::KafkaAuthenticationEvent,
    dispute::KafkaDispute, dispute_event::KafkaDisputeEvent, payment_attempt::KafkaPaymentAttempt,
    payment_attempt_event::KafkaPaymentAttemptEvent, payment_intent::KafkaPaymentIntent,
    payment_intent_event::KafkaPaymentIntentEvent, refund::KafkaRefund,
    refund_event::KafkaRefundEvent,
};
use crate::{services::kafka::fraud_check::KafkaFraudCheck, types::storage::Dispute};

// Using message queue result here to avoid confusion with Kafka result provided by library
pub type MQResult<T> = CustomResult<T, KafkaError>;
use crate::db::kafka_store::TenantID;

pub trait KafkaMessage
where
    Self: Serialize + std::fmt::Debug,
{
    fn value(&self) -> MQResult<Vec<u8>> {
        // Add better error logging here
        serde_json::to_vec(&self).change_context(KafkaError::GenericError)
    }

    fn key(&self) -> String;

    fn event_type(&self) -> EventType;

    fn creation_timestamp(&self) -> Option<i64> {
        None
    }
}

#[derive(serde::Serialize, Debug)]
struct KafkaEvent<'a, T: KafkaMessage> {
    #[serde(flatten)]
    event: &'a T,
    sign_flag: i32,
    tenant_id: TenantID,
    clickhouse_database: Option<String>,
}

impl<'a, T: KafkaMessage> KafkaEvent<'a, T> {
    fn new(event: &'a T, tenant_id: TenantID, clickhouse_database: Option<String>) -> Self {
        Self {
            event,
            sign_flag: 1,
            tenant_id,
            clickhouse_database,
        }
    }
    fn old(event: &'a T, tenant_id: TenantID, clickhouse_database: Option<String>) -> Self {
        Self {
            event,
            sign_flag: -1,
            tenant_id,
            clickhouse_database,
        }
    }
}

impl<T: KafkaMessage> KafkaMessage for KafkaEvent<'_, T> {
    fn key(&self) -> String {
        self.event.key()
    }

    fn event_type(&self) -> EventType {
        self.event.event_type()
    }

    fn creation_timestamp(&self) -> Option<i64> {
        self.event.creation_timestamp()
    }
}

#[derive(serde::Serialize, Debug)]
struct KafkaConsolidatedLog<'a, T: KafkaMessage> {
    #[serde(flatten)]
    event: &'a T,
    tenant_id: TenantID,
}

#[derive(serde::Serialize, Debug)]
struct KafkaConsolidatedEvent<'a, T: KafkaMessage> {
    log: KafkaConsolidatedLog<'a, T>,
    log_type: EventType,
}

impl<'a, T: KafkaMessage> KafkaConsolidatedEvent<'a, T> {
    fn new(event: &'a T, tenant_id: TenantID) -> Self {
        Self {
            log: KafkaConsolidatedLog { event, tenant_id },
            log_type: event.event_type(),
        }
    }
}

impl<T: KafkaMessage> KafkaMessage for KafkaConsolidatedEvent<'_, T> {
    fn key(&self) -> String {
        self.log.event.key()
    }

    fn event_type(&self) -> EventType {
        EventType::Consolidated
    }

    fn creation_timestamp(&self) -> Option<i64> {
        self.log.event.creation_timestamp()
    }
}

#[derive(Debug, serde::Deserialize, Clone, Default)]
#[serde(default)]
pub struct KafkaSettings {
    brokers: Vec<String>,
    fraud_check_analytics_topic: String,
    intent_analytics_topic: String,
    attempt_analytics_topic: String,
    refund_analytics_topic: String,
    api_logs_topic: String,
    connector_logs_topic: String,
    outgoing_webhook_logs_topic: String,
    dispute_analytics_topic: String,
    audit_events_topic: String,
    #[cfg(feature = "payouts")]
    payout_analytics_topic: String,
    consolidated_events_topic: String,
    authentication_analytics_topic: String,
    routing_logs_topic: String,
    revenue_recovery_topic: String,
}

impl KafkaSettings {
    pub fn validate(&self) -> Result<(), crate::core::errors::ApplicationError> {
        use common_utils::ext_traits::ConfigExt;

        use crate::core::errors::ApplicationError;

        common_utils::fp_utils::when(self.brokers.is_empty(), || {
            Err(ApplicationError::InvalidConfigurationValueError(
                "Kafka brokers must not be empty".into(),
            ))
        })?;

        common_utils::fp_utils::when(self.intent_analytics_topic.is_default_or_empty(), || {
            Err(ApplicationError::InvalidConfigurationValueError(
                "Kafka Intent Analytics topic must not be empty".into(),
            ))
        })?;

        common_utils::fp_utils::when(self.attempt_analytics_topic.is_default_or_empty(), || {
            Err(ApplicationError::InvalidConfigurationValueError(
                "Kafka Attempt Analytics topic must not be empty".into(),
            ))
        })?;

        common_utils::fp_utils::when(self.refund_analytics_topic.is_default_or_empty(), || {
            Err(ApplicationError::InvalidConfigurationValueError(
                "Kafka Refund Analytics topic must not be empty".into(),
            ))
        })?;

        common_utils::fp_utils::when(self.api_logs_topic.is_default_or_empty(), || {
            Err(ApplicationError::InvalidConfigurationValueError(
                "Kafka API event Analytics topic must not be empty".into(),
            ))
        })?;

        common_utils::fp_utils::when(self.connector_logs_topic.is_default_or_empty(), || {
            Err(ApplicationError::InvalidConfigurationValueError(
                "Kafka Connector Logs topic must not be empty".into(),
            ))
        })?;

        common_utils::fp_utils::when(
            self.outgoing_webhook_logs_topic.is_default_or_empty(),
            || {
                Err(ApplicationError::InvalidConfigurationValueError(
                    "Kafka Outgoing Webhook Logs topic must not be empty".into(),
                ))
            },
        )?;

        common_utils::fp_utils::when(self.dispute_analytics_topic.is_default_or_empty(), || {
            Err(ApplicationError::InvalidConfigurationValueError(
                "Kafka Dispute Logs topic must not be empty".into(),
            ))
        })?;

        common_utils::fp_utils::when(self.audit_events_topic.is_default_or_empty(), || {
            Err(ApplicationError::InvalidConfigurationValueError(
                "Kafka Audit Events topic must not be empty".into(),
            ))
        })?;

        #[cfg(feature = "payouts")]
        common_utils::fp_utils::when(self.payout_analytics_topic.is_default_or_empty(), || {
            Err(ApplicationError::InvalidConfigurationValueError(
                "Kafka Payout Analytics topic must not be empty".into(),
            ))
        })?;

        common_utils::fp_utils::when(self.consolidated_events_topic.is_default_or_empty(), || {
            Err(ApplicationError::InvalidConfigurationValueError(
                "Consolidated Events topic must not be empty".into(),
            ))
        })?;

        common_utils::fp_utils::when(
            self.authentication_analytics_topic.is_default_or_empty(),
            || {
                Err(ApplicationError::InvalidConfigurationValueError(
                    "Kafka Authentication Analytics topic must not be empty".into(),
                ))
            },
        )?;

        common_utils::fp_utils::when(self.routing_logs_topic.is_default_or_empty(), || {
            Err(ApplicationError::InvalidConfigurationValueError(
                "Kafka Routing Logs topic must not be empty".into(),
            ))
        })?;

        Ok(())
    }
}

#[derive(Clone, Debug)]
pub struct KafkaProducer {
    producer: Arc<RdKafkaProducer>,
    intent_analytics_topic: String,
    fraud_check_analytics_topic: String,
    attempt_analytics_topic: String,
    refund_analytics_topic: String,
    api_logs_topic: String,
    connector_logs_topic: String,
    outgoing_webhook_logs_topic: String,
    dispute_analytics_topic: String,
    audit_events_topic: String,
    #[cfg(feature = "payouts")]
    payout_analytics_topic: String,
    consolidated_events_topic: String,
    authentication_analytics_topic: String,
    ckh_database_name: Option<String>,
    routing_logs_topic: String,
    revenue_recovery_topic: String,
}

struct RdKafkaProducer(ThreadedProducer<DefaultProducerContext>);

impl std::fmt::Debug for RdKafkaProducer {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str("RdKafkaProducer")
    }
}

#[derive(Debug, Clone, thiserror::Error)]
pub enum KafkaError {
    #[error("Generic Kafka Error")]
    GenericError,
    #[error("Kafka not implemented")]
    NotImplemented,
    #[error("Kafka Initialization Error")]
    InitializationError,
}

#[allow(unused)]
impl KafkaProducer {
    pub fn set_tenancy(&mut self, tenant_config: &dyn TenantConfig) {
        self.ckh_database_name = Some(tenant_config.get_clickhouse_database().to_string());
    }

    pub async fn create(conf: &KafkaSettings) -> MQResult<Self> {
        Ok(Self {
            producer: Arc::new(RdKafkaProducer(
                ThreadedProducer::from_config(
                    rdkafka::ClientConfig::new().set("bootstrap.servers", conf.brokers.join(",")),
                )
                .change_context(KafkaError::InitializationError)?,
            )),

            fraud_check_analytics_topic: conf.fraud_check_analytics_topic.clone(),
            intent_analytics_topic: conf.intent_analytics_topic.clone(),
            attempt_analytics_topic: conf.attempt_analytics_topic.clone(),
            refund_analytics_topic: conf.refund_analytics_topic.clone(),
            api_logs_topic: conf.api_logs_topic.clone(),
            connector_logs_topic: conf.connector_logs_topic.clone(),
            outgoing_webhook_logs_topic: conf.outgoing_webhook_logs_topic.clone(),
            dispute_analytics_topic: conf.dispute_analytics_topic.clone(),
            audit_events_topic: conf.audit_events_topic.clone(),
            #[cfg(feature = "payouts")]
            payout_analytics_topic: conf.payout_analytics_topic.clone(),
            consolidated_events_topic: conf.consolidated_events_topic.clone(),
            authentication_analytics_topic: conf.authentication_analytics_topic.clone(),
            ckh_database_name: None,
            routing_logs_topic: conf.routing_logs_topic.clone(),
            revenue_recovery_topic: conf.revenue_recovery_topic.clone(),
        })
    }

    pub fn log_event<T: KafkaMessage>(&self, event: &T) -> MQResult<()> {
        router_env::logger::debug!("Logging Kafka Event {event:?}");
        let topic = self.get_topic(event.event_type());
        self.producer
            .0
            .send(
                BaseRecord::to(topic)
                    .key(&event.key())
                    .payload(&event.value()?)
                    .timestamp(event.creation_timestamp().unwrap_or_else(|| {
                        (OffsetDateTime::now_utc().unix_timestamp_nanos() / 1_000_000)
                            .try_into()
                            .unwrap_or_else(|_| {
                                // kafka producer accepts milliseconds
                                // try converting nanos to millis if that fails convert seconds to millis
                                OffsetDateTime::now_utc().unix_timestamp() * 1_000
                            })
                    })),
            )
            .map_err(|(error, record)| report!(error).attach_printable(format!("{record:?}")))
            .change_context(KafkaError::GenericError)
    }
    pub async fn log_fraud_check(
        &self,
        attempt: &FraudCheck,
        old_attempt: Option<FraudCheck>,
        tenant_id: TenantID,
    ) -> MQResult<()> {
        if let Some(negative_event) = old_attempt {
            self.log_event(&KafkaEvent::old(
                &KafkaFraudCheck::from_storage(&negative_event),
                tenant_id.clone(),
                self.ckh_database_name.clone(),
            ))
            .attach_printable_lazy(|| {
                format!("Failed to add negative fraud check event {negative_event:?}")
            })?;
        };

        self.log_event(&KafkaEvent::new(
            &KafkaFraudCheck::from_storage(attempt),
            tenant_id.clone(),
            self.ckh_database_name.clone(),
        ))
        .attach_printable_lazy(|| {
            format!("Failed to add positive fraud check event {attempt:?}")
        })?;

        self.log_event(&KafkaConsolidatedEvent::new(
            &KafkaFraudCheckEvent::from_storage(attempt),
            tenant_id.clone(),
        ))
        .attach_printable_lazy(|| {
            format!("Failed to add consolidated fraud check  event {attempt:?}")
        })
    }

    pub async fn log_payment_attempt(
        &self,
        attempt: &PaymentAttempt,
        old_attempt: Option<PaymentAttempt>,
        tenant_id: TenantID,
    ) -> MQResult<()> {
        if let Some(negative_event) = old_attempt {
            self.log_event(&KafkaEvent::old(
                &KafkaPaymentAttempt::from_storage(&negative_event),
                tenant_id.clone(),
                self.ckh_database_name.clone(),
            ))
            .attach_printable_lazy(|| {
                format!("Failed to add negative attempt event {negative_event:?}")
            })?;
        };

        self.log_event(&KafkaEvent::new(
            &KafkaPaymentAttempt::from_storage(attempt),
            tenant_id.clone(),
            self.ckh_database_name.clone(),
        ))
        .attach_printable_lazy(|| format!("Failed to add positive attempt event {attempt:?}"))?;

        self.log_event(&KafkaConsolidatedEvent::new(
            &KafkaPaymentAttemptEvent::from_storage(attempt),
            tenant_id.clone(),
        ))
        .attach_printable_lazy(|| format!("Failed to add consolidated attempt event {attempt:?}"))
    }

    pub async fn log_payment_attempt_delete(
        &self,
        delete_old_attempt: &PaymentAttempt,
        tenant_id: TenantID,
    ) -> MQResult<()> {
        self.log_event(&KafkaEvent::old(
            &KafkaPaymentAttempt::from_storage(delete_old_attempt),
            tenant_id.clone(),
            self.ckh_database_name.clone(),
        ))
        .attach_printable_lazy(|| {
            format!("Failed to add negative attempt event {delete_old_attempt:?}")
        })
    }

    pub async fn log_authentication(
        &self,
        authentication: &Authentication,
        old_authentication: Option<Authentication>,
        tenant_id: TenantID,
    ) -> MQResult<()> {
        if let Some(negative_event) = old_authentication {
            self.log_event(&KafkaEvent::old(
                &KafkaAuthentication::from_storage(&negative_event),
                tenant_id.clone(),
                self.ckh_database_name.clone(),
            ))
            .attach_printable_lazy(|| {
                format!("Failed to add negative authentication event {negative_event:?}")
            })?;
        };

        self.log_event(&KafkaEvent::new(
            &KafkaAuthentication::from_storage(authentication),
            tenant_id.clone(),
            self.ckh_database_name.clone(),
        ))
        .attach_printable_lazy(|| {
            format!("Failed to add positive authentication event {authentication:?}")
        })?;

        self.log_event(&KafkaConsolidatedEvent::new(
            &KafkaAuthenticationEvent::from_storage(authentication),
            tenant_id.clone(),
        ))
        .attach_printable_lazy(|| {
            format!("Failed to add consolidated authentication event {authentication:?}")
        })
    }

    pub async fn log_payment_intent(
        &self,
        intent: &PaymentIntent,
        old_intent: Option<PaymentIntent>,
        tenant_id: TenantID,
        infra_values: Option<Value>,
    ) -> MQResult<()> {
        if let Some(negative_event) = old_intent {
            self.log_event(&KafkaEvent::old(
                &KafkaPaymentIntent::from_storage(&negative_event, infra_values.clone()),
                tenant_id.clone(),
                self.ckh_database_name.clone(),
            ))
            .attach_printable_lazy(|| {
                format!("Failed to add negative intent event {negative_event:?}")
            })?;
        };

        self.log_event(&KafkaEvent::new(
            &KafkaPaymentIntent::from_storage(intent, infra_values.clone()),
            tenant_id.clone(),
            self.ckh_database_name.clone(),
        ))
        .attach_printable_lazy(|| format!("Failed to add positive intent event {intent:?}"))?;

        self.log_event(&KafkaConsolidatedEvent::new(
            &KafkaPaymentIntentEvent::from_storage(intent, infra_values.clone()),
            tenant_id.clone(),
        ))
        .attach_printable_lazy(|| format!("Failed to add consolidated intent event {intent:?}"))
    }

    pub async fn log_payment_intent_delete(
        &self,
        delete_old_intent: &PaymentIntent,
        tenant_id: TenantID,
        infra_values: Option<Value>,
    ) -> MQResult<()> {
        self.log_event(&KafkaEvent::old(
            &KafkaPaymentIntent::from_storage(delete_old_intent, infra_values),
            tenant_id.clone(),
            self.ckh_database_name.clone(),
        ))
        .attach_printable_lazy(|| {
            format!("Failed to add negative intent event {delete_old_intent:?}")
        })
    }

    pub async fn log_refund(
        &self,
        refund: &Refund,
        old_refund: Option<Refund>,
        tenant_id: TenantID,
    ) -> MQResult<()> {
        if let Some(negative_event) = old_refund {
            self.log_event(&KafkaEvent::old(
                &KafkaRefund::from_storage(&negative_event),
                tenant_id.clone(),
                self.ckh_database_name.clone(),
            ))
            .attach_printable_lazy(|| {
                format!("Failed to add negative refund event {negative_event:?}")
            })?;
        };

        self.log_event(&KafkaEvent::new(
            &KafkaRefund::from_storage(refund),
            tenant_id.clone(),
            self.ckh_database_name.clone(),
        ))
        .attach_printable_lazy(|| format!("Failed to add positive refund event {refund:?}"))?;

        self.log_event(&KafkaConsolidatedEvent::new(
            &KafkaRefundEvent::from_storage(refund),
            tenant_id.clone(),
        ))
        .attach_printable_lazy(|| format!("Failed to add consolidated refund event {refund:?}"))
    }

    pub async fn log_refund_delete(
        &self,
        delete_old_refund: &Refund,
        tenant_id: TenantID,
    ) -> MQResult<()> {
        self.log_event(&KafkaEvent::old(
            &KafkaRefund::from_storage(delete_old_refund),
            tenant_id.clone(),
            self.ckh_database_name.clone(),
        ))
        .attach_printable_lazy(|| {
            format!("Failed to add negative refund event {delete_old_refund:?}")
        })
    }

    pub async fn log_dispute(
        &self,
        dispute: &Dispute,
        old_dispute: Option<Dispute>,
        tenant_id: TenantID,
    ) -> MQResult<()> {
        if let Some(negative_event) = old_dispute {
            self.log_event(&KafkaEvent::old(
                &KafkaDispute::from_storage(&negative_event),
                tenant_id.clone(),
                self.ckh_database_name.clone(),
            ))
            .attach_printable_lazy(|| {
                format!("Failed to add negative dispute event {negative_event:?}")
            })?;
        };

        self.log_event(&KafkaEvent::new(
            &KafkaDispute::from_storage(dispute),
            tenant_id.clone(),
            self.ckh_database_name.clone(),
        ))
        .attach_printable_lazy(|| format!("Failed to add positive dispute event {dispute:?}"))?;

        self.log_event(&KafkaConsolidatedEvent::new(
            &KafkaDisputeEvent::from_storage(dispute),
            tenant_id.clone(),
        ))
        .attach_printable_lazy(|| format!("Failed to add consolidated dispute event {dispute:?}"))
    }

    pub async fn log_dispute_delete(
        &self,
        delete_old_dispute: &Dispute,
        tenant_id: TenantID,
    ) -> MQResult<()> {
        self.log_event(&KafkaEvent::old(
            &KafkaDispute::from_storage(delete_old_dispute),
            tenant_id.clone(),
            self.ckh_database_name.clone(),
        ))
        .attach_printable_lazy(|| {
            format!("Failed to add negative dispute event {delete_old_dispute:?}")
        })
    }

    #[cfg(feature = "payouts")]
    pub async fn log_payout(
        &self,
        payout: &KafkaPayout<'_>,
        old_payout: Option<KafkaPayout<'_>>,
        tenant_id: TenantID,
    ) -> MQResult<()> {
        if let Some(negative_event) = old_payout {
            self.log_event(&KafkaEvent::old(
                &negative_event,
                tenant_id.clone(),
                self.ckh_database_name.clone(),
            ))
            .attach_printable_lazy(|| {
                format!("Failed to add negative payout event {negative_event:?}")
            })?;
        };
        self.log_event(&KafkaEvent::new(
            payout,
            tenant_id.clone(),
            self.ckh_database_name.clone(),
        ))
        .attach_printable_lazy(|| format!("Failed to add positive payout event {payout:?}"))
    }

    #[cfg(feature = "payouts")]
    pub async fn log_payout_delete(
        &self,
        delete_old_payout: &KafkaPayout<'_>,
        tenant_id: TenantID,
    ) -> MQResult<()> {
        self.log_event(&KafkaEvent::old(
            delete_old_payout,
            tenant_id.clone(),
            self.ckh_database_name.clone(),
        ))
        .attach_printable_lazy(|| {
            format!("Failed to add negative payout event {delete_old_payout:?}")
        })
    }

    pub fn get_topic(&self, event: EventType) -> &str {
        match event {
            EventType::FraudCheck => &self.fraud_check_analytics_topic,
            EventType::ApiLogs => &self.api_logs_topic,
            EventType::PaymentAttempt => &self.attempt_analytics_topic,
            EventType::PaymentIntent => &self.intent_analytics_topic,
            EventType::Refund => &self.refund_analytics_topic,
            EventType::ConnectorApiLogs => &self.connector_logs_topic,
            EventType::OutgoingWebhookLogs => &self.outgoing_webhook_logs_topic,
            EventType::Dispute => &self.dispute_analytics_topic,
            EventType::AuditEvent => &self.audit_events_topic,
            #[cfg(feature = "payouts")]
            EventType::Payout => &self.payout_analytics_topic,
            EventType::Consolidated => &self.consolidated_events_topic,
            EventType::Authentication => &self.authentication_analytics_topic,
            EventType::RoutingApiLogs => &self.routing_logs_topic,
            EventType::RevenueRecovery => &self.revenue_recovery_topic,
        }
    }
}

impl Drop for RdKafkaProducer {
    fn drop(&mut self) {
        // Flush the producer to send any pending messages
        match self.0.flush(rdkafka::util::Timeout::After(
            std::time::Duration::from_secs(5),
        )) {
            Ok(_) => router_env::logger::info!("Kafka events flush Successful"),
            Err(error) => router_env::logger::error!("Failed to flush Kafka Events {error:?}"),
        }
    }
}

impl MessagingInterface for KafkaProducer {
    type MessageClass = EventType;

    fn send_message<T>(
        &self,
        data: T,
        metadata: HashMap<String, String>,
        timestamp: PrimitiveDateTime,
    ) -> error_stack::Result<(), EventsError>
    where
        T: Message<Class = Self::MessageClass> + masking::ErasedMaskSerialize,
    {
        let topic = self.get_topic(data.get_message_class());
        let json_data = data
            .masked_serialize()
            .and_then(|mut value| {
                if let Value::Object(ref mut map) = value {
                    if let Some(db_name) = self.ckh_database_name.clone() {
                        map.insert("clickhouse_database".to_string(), Value::String(db_name));
                    }
                }
                serde_json::to_vec(&value)
            })
            .change_context(EventsError::SerializationError)?;
        let mut headers = OwnedHeaders::new();
        for (k, v) in metadata.iter() {
            headers = headers.insert(Header {
                key: k.as_str(),
                value: Some(v),
            });
        }
        headers = headers.insert(Header {
            key: "clickhouse_database",
            value: self.ckh_database_name.as_ref(),
        });
        self.producer
            .0
            .send(
                BaseRecord::to(topic)
                    .key(&data.identifier())
                    .payload(&json_data)
                    .headers(headers)
                    .timestamp(
                        (timestamp.assume_utc().unix_timestamp_nanos() / 1_000_000)
                            .to_i64()
                            .unwrap_or_else(|| {
                                // kafka producer accepts milliseconds
                                // try converting nanos to millis if that fails convert seconds to millis
                                timestamp.assume_utc().unix_timestamp() * 1_000
                            }),
                    ),
            )
            .map_err(|(error, record)| report!(error).attach_printable(format!("{record:?}")))
            .change_context(KafkaError::GenericError)
            .change_context(EventsError::PublishError)
    }
}


// Struct: NexinetsTransaction
// File: crates/hyperswitch_connectors/src/connectors/nexinets/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct NexinetsTransaction


                            .payment_intent
                            .enable_partial_authorization,
                        enable_overcapture: payment_data.payment_intent.enable_overcapture,
                    })),
                    &m_key_store,
                    storage_scheme,
                )
                .map(|x| x.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound))
                .await
            }
            .in_current_span(),
        );

        let customer_fut =
            if let Some((updated_customer, customer)) = updated_customer.zip(customer) {
                let m_customer_merchant_id = customer.merchant_id.to_owned();
                let m_key_store = key_store.clone();
                let m_updated_customer = updated_customer.clone();
                let session_state = state.clone();
                let m_db = session_state.store.clone();
                let key_manager_state = state.into();
                tokio::spawn(
                    async move {
                        let m_customer_customer_id = customer.customer_id.to_owned();
                        m_db.update_customer_by_customer_id_merchant_id(
                            &key_manager_state,
                            m_customer_customer_id,
                            m_customer_merchant_id,
                            customer,
                            m_updated_customer,
                            &m_key_store,
                            storage_scheme,
                        )
                        .await
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable("Failed to update CustomerConnector in customer")?;

                        Ok::<_, error_stack::Report<errors::ApiErrorResponse>>(())
                    }
                    .in_current_span(),
                )
            } else {
                tokio::spawn(
                    async move { Ok::<_, error_stack::Report<errors::ApiErrorResponse>>(()) }
                        .in_current_span(),
                )
            };

        let (payment_intent, payment_attempt, _) = tokio::try_join!(
            utils::flatten_join_error(payment_intent_fut),
            utils::flatten_join_error(payment_attempt_fut),
            utils::flatten_join_error(customer_fut)
        )?;

        payment_data.payment_intent = payment_intent;
        payment_data.payment_attempt = payment_attempt;

        let client_src = payment_data.payment_attempt.client_source.clone();
        let client_ver = payment_data.payment_attempt.client_version.clone();

        let frm_message = payment_data.frm_message.clone();
        req_state
            .event_context
            .event(AuditEvent::new(AuditEventType::PaymentConfirm {
                client_src,
                client_ver,
                frm_message: Box::new(frm_message),
            }))
            .with(payment_data.to_event())
            .emit();
        Ok((Box::new(self), payment_data))
    }
}

#[async_trait]
impl<F: Send + Clone + Sync> ValidateRequest<F, api::PaymentsRequest, PaymentData<F>>
    for PaymentConfirm
{
    #[instrument(skip_all)]
    fn validate_request<'a, 'b>(
        &'b self,
        request: &api::PaymentsRequest,
        merchant_context: &'a domain::MerchantContext,
    ) -> RouterResult<(PaymentConfirmOperation<'b, F>, operations::ValidateResult)> {
        helpers::validate_customer_information(request)?;

        if let Some(amount) = request.amount {
            helpers::validate_max_amount(amount)?;
        }

        let request_merchant_id = request.merchant_id.as_ref();
        helpers::validate_merchant_id(
            merchant_context.get_merchant_account().get_id(),
            request_merchant_id,
        )
        .change_context(errors::ApiErrorResponse::InvalidDataFormat {
            field_name: "merchant_id".to_string(),
            expected_format: "merchant_id from merchant account".to_string(),
        })?;

        helpers::validate_payment_method_fields_present(request)?;

        let _mandate_type =
            helpers::validate_mandate(request, payments::is_operation_confirm(self))?;

        helpers::validate_recurring_details_and_token(
            &request.recurring_details,
            &request.payment_token,
            &request.mandate_id,
        )?;
        request.validate_stored_credential().change_context(
            errors::ApiErrorResponse::InvalidRequestData {
                message:
                    "is_stored_credential should be true when reusing stored payment method data"
                        .to_string(),
            },
        )?;
        let payment_id = request
            .payment_id
            .clone()
            .ok_or(report!(errors::ApiErrorResponse::PaymentNotFound))?;

        let _request_straight_through: Option<api::routing::StraightThroughAlgorithm> = request
            .routing
            .clone()
            .map(|val| val.parse_value("RoutingAlgorithm"))
            .transpose()
            .change_context(errors::ApiErrorResponse::InvalidRequestData {
                message: "Invalid straight through routing rules format".to_string(),
            })
            .attach_printable("Invalid straight through routing rules format")?;

        Ok((
            Box::new(self),
            operations::ValidateResult {
                merchant_id: merchant_context.get_merchant_account().get_id().to_owned(),
                payment_id,
                storage_scheme: merchant_context.get_merchant_account().storage_scheme,
                requeue: matches!(
                    request.retry_action,
                    Some(api_models::enums::RetryAction::Requeue)
                ),
            },
        ))
    }
}


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

impl ConnectorPreAuthentication for for Threedsecureio


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

pub fn server(state: AppState) -> Scope


// File: crates/hyperswitch_connectors/src/types.rs
// Module: hyperswitch_connectors

#[cfg(feature = "v2")]
use hyperswitch_domain_models::router_data_v2::RouterDataV2;
#[cfg(feature = "payouts")]
use hyperswitch_domain_models::types::{PayoutsData, PayoutsResponseData};
use hyperswitch_domain_models::{
    router_data::{AccessToken, RouterData},
    router_flow_types::{
        authentication::{
            Authentication, PostAuthentication, PreAuthentication, PreAuthenticationVersionCall,
        },
        Accept, AccessTokenAuth, Authorize, Capture, CreateOrder, Defend, Dsync, Evidence, Fetch,
        PSync, PostProcessing, PreProcessing, Retrieve, Session, Upload, Void,
    },
    router_request_types::{
        authentication::{
            ConnectorAuthenticationRequestData, ConnectorPostAuthenticationRequestData,
            PreAuthNRequestData,
        },
        AcceptDisputeRequestData, AccessTokenRequestData, CreateOrderRequestData,
        DefendDisputeRequestData, DisputeSyncData, FetchDisputesRequestData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsPostProcessingData,
        PaymentsPreProcessingData, PaymentsSessionData, PaymentsSyncData, RefundsData,
        RetrieveFileRequestData, SubmitEvidenceRequestData, UploadFileRequestData,
    },
    router_response_types::{
        AcceptDisputeResponse, AuthenticationResponseData, DefendDisputeResponse,
        DisputeSyncResponse, FetchDisputesResponse, PaymentsResponseData, RefundsResponseData,
        RetrieveFileResponse, SubmitEvidenceResponse, UploadFileResponse,
    },
};
#[cfg(feature = "frm")]
use hyperswitch_domain_models::{
    router_flow_types::{Checkout, Fulfillment, RecordReturn, Sale, Transaction},
    router_request_types::fraud_check::{
        FraudCheckCheckoutData, FraudCheckFulfillmentData, FraudCheckRecordReturnData,
        FraudCheckSaleData, FraudCheckTransactionData,
    },
    router_response_types::fraud_check::FraudCheckResponseData,
};
use hyperswitch_interfaces::api::ConnectorIntegration;

pub(crate) type PaymentsSyncResponseRouterData<R> =
    ResponseRouterData<PSync, R, PaymentsSyncData, PaymentsResponseData>;
pub(crate) type PaymentsResponseRouterData<R> =
    ResponseRouterData<Authorize, R, PaymentsAuthorizeData, PaymentsResponseData>;
pub(crate) type PaymentsCaptureResponseRouterData<R> =
    ResponseRouterData<Capture, R, PaymentsCaptureData, PaymentsResponseData>;
pub(crate) type RefundsResponseRouterData<F, R> =
    ResponseRouterData<F, R, RefundsData, RefundsResponseData>;
pub(crate) type RefreshTokenRouterData =
    RouterData<AccessTokenAuth, AccessTokenRequestData, AccessToken>;

pub(crate) type PaymentsCancelResponseRouterData<R> =
    ResponseRouterData<Void, R, PaymentsCancelData, PaymentsResponseData>;
pub(crate) type PaymentsPreprocessingResponseRouterData<R> =
    ResponseRouterData<PreProcessing, R, PaymentsPreProcessingData, PaymentsResponseData>;
pub(crate) type PaymentsSessionResponseRouterData<R> =
    ResponseRouterData<Session, R, PaymentsSessionData, PaymentsResponseData>;
pub(crate) type CreateOrderResponseRouterData<R> =
    ResponseRouterData<CreateOrder, R, CreateOrderRequestData, PaymentsResponseData>;

pub(crate) type AcceptDisputeRouterData =
    RouterData<Accept, AcceptDisputeRequestData, AcceptDisputeResponse>;
pub(crate) type SubmitEvidenceRouterData =
    RouterData<Evidence, SubmitEvidenceRequestData, SubmitEvidenceResponse>;
pub(crate) type UploadFileRouterData =
    RouterData<Upload, UploadFileRequestData, UploadFileResponse>;
pub(crate) type DefendDisputeRouterData =
    RouterData<Defend, DefendDisputeRequestData, DefendDisputeResponse>;
pub(crate) type FetchDisputeRouterData =
    RouterData<Fetch, FetchDisputesRequestData, FetchDisputesResponse>;
pub(crate) type DisputeSyncRouterData = RouterData<Dsync, DisputeSyncData, DisputeSyncResponse>;

#[cfg(feature = "payouts")]
pub(crate) type PayoutsResponseRouterData<F, R> =
    ResponseRouterData<F, R, PayoutsData, PayoutsResponseData>;

// TODO: Remove `ResponseRouterData` from router crate after all the related type aliases are moved to this crate.
pub(crate) struct ResponseRouterData<Flow, R, Request, Response> {
    pub(crate) response: R,
    pub(crate) data: RouterData<Flow, Request, Response>,
    pub(crate) http_code: u16,
}
#[cfg(feature = "frm")]
pub(crate) type FrmFulfillmentRouterData =
    RouterData<Fulfillment, FraudCheckFulfillmentData, FraudCheckResponseData>;
#[cfg(feature = "frm")]
pub(crate) type FrmCheckoutType =
    dyn ConnectorIntegration<Checkout, FraudCheckCheckoutData, FraudCheckResponseData>;
#[cfg(feature = "frm")]
pub(crate) type FrmTransactionType =
    dyn ConnectorIntegration<Transaction, FraudCheckTransactionData, FraudCheckResponseData>;
#[cfg(feature = "frm")]
pub(crate) type FrmTransactionRouterData =
    RouterData<Transaction, FraudCheckTransactionData, FraudCheckResponseData>;
#[cfg(feature = "frm")]
pub(crate) type FrmFulfillmentType =
    dyn ConnectorIntegration<Fulfillment, FraudCheckFulfillmentData, FraudCheckResponseData>;
#[cfg(feature = "frm")]
pub(crate) type FrmCheckoutRouterData =
    RouterData<Checkout, FraudCheckCheckoutData, FraudCheckResponseData>;
#[cfg(feature = "v2")]
pub(crate) struct ResponseRouterDataV2<Flow, R, ResourceCommonData, Request, Response> {
    pub response: R,
    pub data: RouterDataV2<Flow, ResourceCommonData, Request, Response>,
    #[allow(dead_code)] // Used for metadata passing but this is not read
    pub http_code: u16,
}

pub(crate) type PreAuthNRouterData =
    RouterData<PreAuthentication, PreAuthNRequestData, AuthenticationResponseData>;
pub(crate) type PreAuthNVersionCallRouterData =
    RouterData<PreAuthenticationVersionCall, PreAuthNRequestData, AuthenticationResponseData>;
pub(crate) type ConnectorAuthenticationRouterData =
    RouterData<Authentication, ConnectorAuthenticationRequestData, AuthenticationResponseData>;
pub(crate) type ConnectorPostAuthenticationRouterData = RouterData<
    PostAuthentication,
    ConnectorPostAuthenticationRequestData,
    AuthenticationResponseData,
>;
pub(crate) type ConnectorAuthenticationType = dyn ConnectorIntegration<
    Authentication,
    ConnectorAuthenticationRequestData,
    AuthenticationResponseData,
>;
pub(crate) type ConnectorPostAuthenticationType = dyn ConnectorIntegration<
    PostAuthentication,
    ConnectorPostAuthenticationRequestData,
    AuthenticationResponseData,
>;
pub(crate) type ConnectorPreAuthenticationType =
    dyn ConnectorIntegration<PreAuthentication, PreAuthNRequestData, AuthenticationResponseData>;
pub(crate) type ConnectorPreAuthenticationVersionCallType = dyn ConnectorIntegration<
    PreAuthenticationVersionCall,
    PreAuthNRequestData,
    AuthenticationResponseData,
>;

pub(crate) type PaymentsPostProcessingRouterData =
    RouterData<PostProcessing, PaymentsPostProcessingData, PaymentsResponseData>;
#[cfg(feature = "frm")]
pub(crate) type FrmSaleRouterData = RouterData<Sale, FraudCheckSaleData, FraudCheckResponseData>;
#[cfg(feature = "frm")]
pub(crate) type FrmRecordReturnRouterData =
    RouterData<RecordReturn, FraudCheckRecordReturnData, FraudCheckResponseData>;
#[cfg(feature = "frm")]
pub(crate) type FrmRecordReturnType =
    dyn ConnectorIntegration<RecordReturn, FraudCheckRecordReturnData, FraudCheckResponseData>;
#[cfg(feature = "frm")]
pub(crate) type FrmSaleType =
    dyn ConnectorIntegration<Sale, FraudCheckSaleData, FraudCheckResponseData>;

pub(crate) type RetrieveFileRouterData =
    RouterData<Retrieve, RetrieveFileRequestData, RetrieveFileResponse>;

#[cfg(feature = "payouts")]
pub(crate) trait PayoutIndividualDetailsExt {
    type Error;
    fn get_external_account_account_holder_type(&self) -> Result<String, Self::Error>;
}

#[cfg(feature = "payouts")]
impl PayoutIndividualDetailsExt for api_models::payouts::PayoutIndividualDetails {
    type Error = error_stack::Report<hyperswitch_interfaces::errors::ConnectorError>;
    fn get_external_account_account_holder_type(&self) -> Result<String, Self::Error> {
        self.external_account_account_holder_type
            .clone()
            .ok_or_else(crate::utils::missing_field_err(
                "external_account_account_holder_type",
            ))
    }
}


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

use common_utils::events::{ApiEventMetric, ApiEventsType};

use crate::routing::{
    ContractBasedRoutingPayloadWrapper, ContractBasedRoutingSetupPayloadWrapper,
    CreateDynamicRoutingWrapper, DynamicRoutingUpdateConfigQuery, EliminationRoutingPayloadWrapper,
    LinkedRoutingConfigRetrieveResponse, MerchantRoutingAlgorithm, ProfileDefaultRoutingConfig,
    RoutingAlgorithmId, RoutingConfigRequest, RoutingDictionaryRecord, RoutingKind,
    RoutingLinkWrapper, RoutingPayloadWrapper, RoutingRetrieveLinkQuery,
    RoutingRetrieveLinkQueryWrapper, RoutingRetrieveQuery, RoutingVolumeSplit,
    RoutingVolumeSplitResponse, RoutingVolumeSplitWrapper, RuleMigrationError, RuleMigrationQuery,
    RuleMigrationResponse, RuleMigrationResult, SuccessBasedRoutingConfig,
    SuccessBasedRoutingPayloadWrapper, ToggleDynamicRoutingPath, ToggleDynamicRoutingQuery,
    ToggleDynamicRoutingWrapper,
};

impl ApiEventMetric for RoutingKind {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for MerchantRoutingAlgorithm {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for RoutingAlgorithmId {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for RoutingDictionaryRecord {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for LinkedRoutingConfigRetrieveResponse {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for RoutingPayloadWrapper {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}
impl ApiEventMetric for ProfileDefaultRoutingConfig {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for RoutingRetrieveQuery {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for RoutingConfigRequest {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for RoutingRetrieveLinkQuery {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for RoutingLinkWrapper {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}
impl ApiEventMetric for RoutingRetrieveLinkQueryWrapper {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for ToggleDynamicRoutingQuery {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for SuccessBasedRoutingConfig {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for SuccessBasedRoutingPayloadWrapper {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for EliminationRoutingPayloadWrapper {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for ContractBasedRoutingPayloadWrapper {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for ContractBasedRoutingSetupPayloadWrapper {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for ToggleDynamicRoutingWrapper {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for CreateDynamicRoutingWrapper {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for DynamicRoutingUpdateConfigQuery {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for RoutingVolumeSplitWrapper {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for ToggleDynamicRoutingPath {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for RoutingVolumeSplitResponse {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for RoutingVolumeSplit {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for RuleMigrationQuery {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for RuleMigrationResponse {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for RuleMigrationResult {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for RuleMigrationError {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for crate::open_router::DecideGatewayResponse {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for crate::open_router::OpenRouterDecideGatewayRequest {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for crate::open_router::UpdateScorePayload {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}

impl ApiEventMetric for crate::open_router::UpdateScoreResponse {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Routing)
    }
}


// Struct: ChatConversation
// File: crates/api_models/src/chat.rs
// Module: api_models
// Implementations: 0

pub struct ChatConversation


// Function: payouts_list_profile
// File: crates/router/src/routes/payouts.rs
// Module: router

pub fn payouts_list_profile(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Query<payout_types::PayoutListConstraints>,
) -> HttpResponse


// Struct: PollId
// File: crates/router/src/types/api/poll.rs
// Module: router
// Implementations: 0

pub struct PollId


// Struct: CardPaymentRequest
// File: crates/hyperswitch_connectors/src/connectors/elavon/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CardPaymentRequest


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

pub struct PaymentMethodCollectLinkRequest


// Function: get_tenant_ids
// File: crates/drainer/src/settings.rs
// Module: drainer

pub fn get_tenant_ids(&self) -> Vec<id_type::TenantId>


// Implementation: impl InputData
// File: crates/hsdev/src/input_file.rs
// Module: hsdev
// Methods: 2 total (2 public)

impl InputData


// Function: update_user_role
// File: crates/router/src/routes/user_role.rs
// Module: router

pub fn update_user_role(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<user_role_api::UpdateUserRoleRequest>,
) -> HttpResponse


// Struct: RazorpayPaymentsRequest
// File: crates/hyperswitch_connectors/src/connectors/razorpay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct RazorpayPaymentsRequest


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

pub fn analyze_program(js_program: JsValue) -> JsResult


// Function: new
// File: crates/hyperswitch_connectors/src/connectors/multisafepay.rs
// Module: hyperswitch_connectors

pub fn new() -> &'static Self


// Implementation: impl PaymentMetricAccumulator for for FailureReasonsDistributionAccumulator
// File: crates/analytics/src/payments/accumulator.rs
// Module: analytics
// Methods: 2 total (0 public)

impl PaymentMetricAccumulator for for FailureReasonsDistributionAccumulator


// Struct: MonerisAuthResponse
// File: crates/hyperswitch_connectors/src/connectors/moneris/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct MonerisAuthResponse


// File: crates/hyperswitch_connectors/src/connectors/gigadat/transformers.rs
// Module: hyperswitch_connectors
// Public structs: 12

use common_enums::{enums, Currency};
use common_utils::{
    id_type,
    pii::{self, Email, IpAddress},
    request::Method,
    types::FloatMajorUnit,
};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    payment_method_data::{BankRedirectData, PaymentMethodData},
    router_data::{ConnectorAuthType, RouterData},
    router_flow_types::refunds::Execute,
    router_request_types::ResponseId,
    router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData},
    types::{PaymentsAuthorizeRouterData, RefundsRouterData},
};
use hyperswitch_interfaces::errors;
use masking::{PeekInterface, Secret};
use serde::{Deserialize, Serialize};

use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    utils::{self, BrowserInformationData, PaymentsAuthorizeRequestData, RouterData as _},
};

pub struct GigadatRouterData<T> {
    pub amount: FloatMajorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc.
    pub router_data: T,
}

impl<T> From<(FloatMajorUnit, T)> for GigadatRouterData<T> {
    fn from((amount, item): (FloatMajorUnit, T)) -> Self {
        Self {
            amount,
            router_data: item,
        }
    }
}

const CONNECTOR_BASE_URL: &str = "https://interac.express-connect.com/";

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

impl TryFrom<&Option<pii::SecretSerdeValue>> for GigadatConnectorMetadataObject {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(meta_data: &Option<pii::SecretSerdeValue>) -> Result<Self, Self::Error> {
        let metadata: Self = utils::to_connector_meta_from_secret::<Self>(meta_data.clone())
            .change_context(errors::ConnectorError::InvalidConnectorConfig {
                config: "merchant_connector_account.metadata",
            })?;
        Ok(metadata)
    }
}

// CPI (Combined Pay-in) Request Structure for Gigadat
#[derive(Debug, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct GigadatCpiRequest {
    pub user_id: id_type::CustomerId,
    pub site: String,
    pub user_ip: Secret<String, IpAddress>,
    pub currency: Currency,
    pub amount: FloatMajorUnit,
    pub transaction_id: String,
    #[serde(rename = "type")]
    pub transaction_type: GidadatTransactionType,
    pub sandbox: bool,
    pub name: Secret<String>,
    pub email: Email,
    pub mobile: Secret<String>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "UPPERCASE")]
pub enum GidadatTransactionType {
    Cpi,
}

impl TryFrom<&GigadatRouterData<&PaymentsAuthorizeRouterData>> for GigadatCpiRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &GigadatRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        let metadata: GigadatConnectorMetadataObject =
            utils::to_connector_meta_from_secret(item.router_data.connector_meta_data.clone())
                .change_context(errors::ConnectorError::InvalidConnectorConfig {
                    config: "merchant_connector_account.metadata",
                })?;
        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::BankRedirect(BankRedirectData::Interac { .. }) => {
                let router_data = item.router_data;
                let name = router_data.get_billing_full_name()?;
                let email = router_data.get_billing_email()?;
                let mobile = router_data.get_billing_phone_number()?;
                let currency = item.router_data.request.currency;
                let sandbox = match item.router_data.test_mode {
                    Some(true) => true,
                    Some(false) | None => false,
                };
                let user_ip = router_data.request.get_browser_info()?.get_ip_address()?;
                Ok(Self {
                    user_id: router_data.get_customer_id()?,
                    site: metadata.site,
                    user_ip,
                    currency,
                    amount: item.amount,
                    transaction_id: router_data.connector_request_reference_id.clone(),
                    transaction_type: GidadatTransactionType::Cpi,
                    name,
                    sandbox,
                    email,
                    mobile,
                })
            }
            PaymentMethodData::BankRedirect(_) => Err(errors::ConnectorError::NotImplemented(
                utils::get_unimplemented_payment_method_error_message("Gigadat"),
            ))?,

            _ => Err(errors::ConnectorError::NotImplemented(
                utils::get_unimplemented_payment_method_error_message("Gigadat"),
            )
            .into()),
        }
    }
}

#[derive(Debug, Clone)]
pub struct GigadatAuthType {
    pub campaign_id: Secret<String>,
    pub access_token: Secret<String>,
    pub security_token: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for GigadatAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::SignatureKey {
                api_key,
                key1,
                api_secret,
            } => Ok(Self {
                security_token: api_secret.to_owned(),
                access_token: api_key.to_owned(),
                campaign_id: key1.to_owned(),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct GigadatPaymentResponse {
    pub token: Secret<String>,
    pub data: GigadatPaymentData,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct GigadatPaymentData {
    pub transaction_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum GigadatPaymentStatus {
    StatusInited,
    StatusSuccess,
    StatusRejected,
    StatusRejected1,
    StatusExpired,
    StatusAborted1,
    StatusPending,
    StatusFailed,
}

impl From<GigadatPaymentStatus> for enums::AttemptStatus {
    fn from(item: GigadatPaymentStatus) -> Self {
        match item {
            GigadatPaymentStatus::StatusSuccess => Self::Charged,
            GigadatPaymentStatus::StatusInited | GigadatPaymentStatus::StatusPending => {
                Self::Pending
            }
            GigadatPaymentStatus::StatusRejected
            | GigadatPaymentStatus::StatusExpired
            | GigadatPaymentStatus::StatusRejected1
            | GigadatPaymentStatus::StatusAborted1
            | GigadatPaymentStatus::StatusFailed => Self::Failure,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct GigadatTransactionStatusResponse {
    pub status: GigadatPaymentStatus,
}

impl<F, T> TryFrom<ResponseRouterData<F, GigadatPaymentResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, GigadatPaymentResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        // Will be raising a sepearte PR to populate a field connect_base_url in routerData and use it here
        let base_url = CONNECTOR_BASE_URL;

        let redirect_url = format!(
            "{}webflow?transaction={}&token={}",
            base_url,
            item.data.connector_request_reference_id,
            item.response.token.peek()
        );

        let redirection_data = Some(RedirectForm::Form {
            endpoint: redirect_url,
            method: Method::Get,
            form_fields: Default::default(),
        });
        Ok(Self {
            status: enums::AttemptStatus::AuthenticationPending,
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.data.transaction_id),
                redirection_data: Box::new(redirection_data),
                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
        })
    }
}

impl<F, T> TryFrom<ResponseRouterData<F, GigadatTransactionStatusResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, GigadatTransactionStatusResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: enums::AttemptStatus::from(item.response.status),
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::NoResponseId,
                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
        })
    }
}

// REFUND :
// Type definition for RefundRequest
#[derive(Default, Debug, Serialize)]
pub struct GigadatRefundRequest {
    pub amount: FloatMajorUnit,
    pub transaction_id: String,
    pub campaign_id: Secret<String>,
}

impl<F> TryFrom<&GigadatRouterData<&RefundsRouterData<F>>> for GigadatRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &GigadatRouterData<&RefundsRouterData<F>>) -> Result<Self, Self::Error> {
        let auth_type = GigadatAuthType::try_from(&item.router_data.connector_auth_type)?;
        Ok(Self {
            amount: item.amount.to_owned(),
            transaction_id: item.router_data.request.connector_transaction_id.clone(),
            campaign_id: auth_type.campaign_id,
        })
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RefundResponse {
    success: bool,
    data: GigadatPaymentData,
}

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> {
        let refund_status = match item.http_code {
            200 => enums::RefundStatus::Success,
            400 | 401 | 422 => enums::RefundStatus::Failure,
            _ => enums::RefundStatus::Pending,
        };

        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.data.transaction_id.to_string(),
                refund_status,
            }),
            ..item.data
        })
    }
}

#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
pub struct GigadatErrorResponse {
    pub err: String,
}

#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
pub struct GigadatRefundErrorResponse {
    pub error: Vec<Error>,
    pub message: String,
}

#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
pub struct Error {
    pub code: Option<String>,
    pub detail: String,
}


// Function: new
// File: crates/smithy-core/src/generator.rs
// Module: smithy-core

pub fn new() -> Self


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

pub struct AddressAirwallex