}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
pub async fn contract_based_dynamic_routing_setup(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id: common_utils::id_type::ProfileId,
    feature_to_enable: routing_types::DynamicRoutingFeatures,
    config: Option<routing_types::ContractBasedRoutingConfig>,
) -> RouterResult<service_api::ApplicationResponse<routing_types::RoutingDictionaryRecord>> {
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let business_profile: domain::Profile = core_utils::validate_and_get_business_profile(
        db,
        key_manager_state,
        merchant_context.get_merchant_key_store(),
        Some(&profile_id),
        merchant_context.get_merchant_account().get_id(),
    )
    .await?
    .get_required_value("Profile")
    .change_context(errors::ApiErrorResponse::ProfileNotFound {
        id: profile_id.get_string_repr().to_owned(),
    })?;

    let mut dynamic_routing_algo_ref: Option<routing_types::DynamicRoutingAlgorithmRef> =
        business_profile
            .dynamic_routing_algorithm
            .clone()
            .map(|val| val.parse_value("DynamicRoutingAlgorithmRef"))
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable(
                "unable to deserialize dynamic routing algorithm ref from business profile",
            )
            .ok()
            .flatten();

    utils::when(
        dynamic_routing_algo_ref
            .as_mut()
            .and_then(|algo| {
                algo.contract_based_routing.as_mut().map(|contract_algo| {
                    *contract_algo.get_enabled_features() == feature_to_enable
                        && contract_algo
                            .clone()
                            .get_algorithm_id_with_timestamp()
                            .algorithm_id
                            .is_some()
                })
            })
            .unwrap_or(false),
        || {
            Err(errors::ApiErrorResponse::PreconditionFailed {
                message: "Contract Routing with specified features is already enabled".to_string(),
            })
        },
    )?;

    if feature_to_enable == routing::DynamicRoutingFeatures::None {
        let algorithm = dynamic_routing_algo_ref
            .clone()
            .get_required_value("dynamic_routing_algo_ref")
            .attach_printable("Failed to get dynamic_routing_algo_ref")?;
        return helpers::disable_dynamic_routing_algorithm(
            &state,
            merchant_context.get_merchant_key_store().clone(),
            business_profile,
            algorithm,
            routing_types::DynamicRoutingType::ContractBasedRouting,
        )
        .await;
    }

    let config = config
        .get_required_value("ContractBasedRoutingConfig")
        .attach_printable("Failed to get ContractBasedRoutingConfig from request")?;

    let merchant_id = business_profile.merchant_id.clone();
    let algorithm_id = common_utils::generate_routing_id_of_default_length();
    let timestamp = common_utils::date_time::now();

    let algo = RoutingAlgorithm {
        algorithm_id: algorithm_id.clone(),
        profile_id: profile_id.clone(),
        merchant_id,
        name: helpers::CONTRACT_BASED_DYNAMIC_ROUTING_ALGORITHM.to_string(),
        description: None,
        kind: diesel_models::enums::RoutingAlgorithmKind::Dynamic,
        algorithm_data: serde_json::json!(config),
        created_at: timestamp,
        modified_at: timestamp,
        algorithm_for: common_enums::TransactionType::Payment,
        decision_engine_routing_id: None,
    };

    // 1. if dynamic_routing_algo_ref already present, insert contract based algo and disable success based
    // 2. if dynamic_routing_algo_ref is not present, create a new dynamic_routing_algo_ref with contract algo set up
    let final_algorithm = if let Some(mut algo) = dynamic_routing_algo_ref {
        algo.update_algorithm_id(
            algorithm_id,
            feature_to_enable,
            routing_types::DynamicRoutingType::ContractBasedRouting,
        );
        if feature_to_enable == routing::DynamicRoutingFeatures::DynamicConnectorSelection {
            algo.disable_algorithm_id(routing_types::DynamicRoutingType::SuccessRateBasedRouting);
        }
        algo
    } else {
        let contract_algo = routing_types::ContractRoutingAlgorithm {
            algorithm_id_with_timestamp: routing_types::DynamicAlgorithmWithTimestamp::new(Some(
                algorithm_id.clone(),
            )),
            enabled_feature: feature_to_enable,
        };
        routing_types::DynamicRoutingAlgorithmRef {
            success_based_algorithm: None,
            elimination_routing_algorithm: None,
            dynamic_routing_volume_split: None,
            contract_based_routing: Some(contract_algo),
            is_merchant_created_in_decision_engine: dynamic_routing_algo_ref
                .as_ref()
                .is_some_and(|algo| algo.is_merchant_created_in_decision_engine),
        }
    };

    // validate the contained mca_ids
    let mut contained_mca = Vec::new();
    if let Some(info_vec) = &config.label_info {
        for info in info_vec {
            utils::when(
                contained_mca.iter().any(|mca_id| mca_id == &info.mca_id),
                || {
                    Err(error_stack::Report::new(
                        errors::ApiErrorResponse::InvalidRequestData {
                            message: "Duplicate mca configuration received".to_string(),
                        },
                    ))
                },
            )?;

            contained_mca.push(info.mca_id.to_owned());
        }

        let validation_futures: Vec<_> = info_vec
            .iter()
            .map(|info| async {
                let mca_id = info.mca_id.clone();
                let label = info.label.clone();
                let mca = db
                    .find_by_merchant_connector_account_merchant_id_merchant_connector_id(
                        key_manager_state,
                        merchant_context.get_merchant_account().get_id(),
                        &mca_id,
                        merchant_context.get_merchant_key_store(),
                    )
                    .await
                    .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                        id: mca_id.get_string_repr().to_owned(),
                    })?;

                utils::when(mca.connector_name != label, || {
                    Err(error_stack::Report::new(
                        errors::ApiErrorResponse::InvalidRequestData {
                            message: "Incorrect mca configuration received".to_string(),
                        },
                    ))
                })?;

                Ok::<_, error_stack::Report<errors::ApiErrorResponse>>(())
            })
            .collect();

        futures::future::try_join_all(validation_futures).await?;
    }

    let record = db
        .insert_routing_algorithm(algo)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to insert record in routing algorithm table")?;

    helpers::update_business_profile_active_dynamic_algorithm_ref(
        db,
        key_manager_state,
        merchant_context.get_merchant_key_store(),
        business_profile,
        final_algorithm,
    )
    .await?;

    let new_record = record.foreign_into();

    metrics::ROUTING_CREATE_SUCCESS_RESPONSE.add(
        1,
        router_env::metric_attributes!(("profile_id", profile_id.get_string_repr().to_string())),
    );
    Ok(service_api::ApplicationResponse::Json(new_record))
}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
pub async fn contract_based_routing_update_configs(
    state: SessionState,
    request: routing_types::ContractBasedRoutingConfig,
    merchant_context: domain::MerchantContext,
    algorithm_id: common_utils::id_type::RoutingId,
    profile_id: common_utils::id_type::ProfileId,
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
    metrics::ROUTING_UPDATE_CONFIG_FOR_PROFILE.add(
        1,
        router_env::metric_attributes!(
            ("profile_id", profile_id.get_string_repr().to_owned()),
            (
                "algorithm_type",
                routing::DynamicRoutingType::ContractBasedRouting.to_string()
            )
        ),
    );
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let dynamic_routing_algo_to_update = db
        .find_routing_algorithm_by_profile_id_algorithm_id(&profile_id, &algorithm_id)
        .await
        .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;

    let mut config_to_update: routing::ContractBasedRoutingConfig = dynamic_routing_algo_to_update
        .algorithm_data
        .parse_value::<routing::ContractBasedRoutingConfig>("ContractBasedRoutingConfig")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("unable to deserialize algorithm data from routing table into ContractBasedRoutingConfig")?;

    // validate the contained mca_ids
    let mut contained_mca = Vec::new();
    if let Some(info_vec) = &request.label_info {
        for info in info_vec {
            let mca = db
                .find_by_merchant_connector_account_merchant_id_merchant_connector_id(
                    key_manager_state,
                    merchant_context.get_merchant_account().get_id(),
                    &info.mca_id,
                    merchant_context.get_merchant_key_store(),
                )
                .await
                .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                    id: info.mca_id.get_string_repr().to_owned(),
                })?;

            utils::when(mca.connector_name != info.label, || {
                Err(errors::ApiErrorResponse::InvalidRequestData {
                    message: "Incorrect mca configuration received".to_string(),
                })
            })?;

            utils::when(
                contained_mca.iter().any(|mca_id| mca_id == &info.mca_id),
                || {
                    Err(error_stack::Report::new(
                        errors::ApiErrorResponse::InvalidRequestData {
                            message: "Duplicate mca configuration received".to_string(),
                        },
                    ))
                },
            )?;

            contained_mca.push(info.mca_id.to_owned());
        }
    }

    config_to_update.update(request);

    let updated_algorithm_id = common_utils::generate_routing_id_of_default_length();
    let timestamp = common_utils::date_time::now();
    let algo = RoutingAlgorithm {
        algorithm_id: updated_algorithm_id,
        profile_id: dynamic_routing_algo_to_update.profile_id,
        merchant_id: dynamic_routing_algo_to_update.merchant_id,
        name: dynamic_routing_algo_to_update.name,
        description: dynamic_routing_algo_to_update.description,
        kind: dynamic_routing_algo_to_update.kind,
        algorithm_data: serde_json::json!(config_to_update),
        created_at: timestamp,
        modified_at: timestamp,
        algorithm_for: dynamic_routing_algo_to_update.algorithm_for,
        decision_engine_routing_id: None,
    };
    let record = db
        .insert_routing_algorithm(algo)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to insert record in routing algorithm table")?;

    // redact cache for contract based routing configs
    let cache_key = format!(
        "{}_{}",
        profile_id.get_string_repr(),
        algorithm_id.get_string_repr()
    );
    let cache_entries_to_redact = vec![cache::CacheKind::ContractBasedDynamicRoutingCache(
        cache_key.into(),
    )];
    let _ = cache::redact_from_redis_and_publish(
        state.store.get_cache_store().as_ref(),
        cache_entries_to_redact,
    )
    .await
    .map_err(|e| router_env::logger::error!("unable to publish into the redact channel for evicting the contract based routing config cache {e:?}"));

    let new_record = record.foreign_into();

    metrics::ROUTING_UPDATE_CONFIG_FOR_PROFILE_SUCCESS_RESPONSE.add(
        1,
        router_env::metric_attributes!(("profile_id", profile_id.get_string_repr().to_owned())),
    );

    state
        .grpc_client
        .dynamic_routing
        .as_ref()
        .async_map(|dr_client| async {
            dr_client
                .contract_based_client
                .invalidate_contracts(
                    profile_id.get_string_repr().into(),
                    state.get_grpc_headers(),
                )
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to invalidate the contract based routing keys")
        })
        .await
        .transpose()?;

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

#[async_trait]
pub trait GetRoutableConnectorsForChoice {
    async fn get_routable_connectors(
        &self,
        db: &dyn StorageInterface,
        business_profile: &domain::Profile,
    ) -> RouterResult<RoutableConnectors>;
}

pub struct StraightThroughAlgorithmTypeSingle(pub serde_json::Value);

#[async_trait]
impl GetRoutableConnectorsForChoice for StraightThroughAlgorithmTypeSingle {
    async fn get_routable_connectors(
        &self,
        _db: &dyn StorageInterface,
        _business_profile: &domain::Profile,
    ) -> RouterResult<RoutableConnectors> {
        let straight_through_routing_algorithm = self
            .0
            .clone()
            .parse_value::<api::routing::StraightThroughAlgorithm>("RoutingAlgorithm")
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to parse the straight through routing algorithm")?;
        let routable_connector = match straight_through_routing_algorithm {
            api::routing::StraightThroughAlgorithm::Single(connector) => {
                vec![*connector]
            }

            api::routing::StraightThroughAlgorithm::Priority(_)
            | api::routing::StraightThroughAlgorithm::VolumeSplit(_) => {
                Err(errors::RoutingError::DslIncorrectSelectionAlgorithm)
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable(
                        "Unsupported algorithm received as a result of static routing",
                    )?
            }
        };
        Ok(RoutableConnectors(routable_connector))
    }
}

pub struct DecideConnector;

#[async_trait]
impl GetRoutableConnectorsForChoice for DecideConnector {
    async fn get_routable_connectors(
        &self,
        db: &dyn StorageInterface,
        business_profile: &domain::Profile,
    ) -> RouterResult<RoutableConnectors> {
        let fallback_config = helpers::get_merchant_default_config(
            db,
            business_profile.get_id().get_string_repr(),
            &common_enums::TransactionType::Payment,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)?;
        Ok(RoutableConnectors(fallback_config))
    }
}

pub struct RoutableConnectors(Vec<routing_types::RoutableConnectorChoice>);

impl RoutableConnectors {
    pub fn filter_network_transaction_id_flow_supported_connectors(
        self,
        nit_connectors: HashSet<String>,
    ) -> Self {
        let connectors = self
            .0
            .into_iter()
            .filter(|routable_connector_choice| {
                nit_connectors.contains(&routable_connector_choice.connector.to_string())
            })
            .collect();
        Self(connectors)
    }

    pub async fn construct_dsl_and_perform_eligibility_analysis<F, D>(
        self,
        state: &SessionState,
        key_store: &domain::MerchantKeyStore,
        payment_data: &D,

        profile_id: &common_utils::id_type::ProfileId,
    ) -> RouterResult<Vec<api::ConnectorData>>
    where
        F: Send + Clone,
        D: OperationSessionGetters<F>,
    {
        let payments_dsl_input = PaymentsDslInput::new(
            payment_data.get_setup_mandate(),
            payment_data.get_payment_attempt(),
            payment_data.get_payment_intent(),
            payment_data.get_payment_method_data(),
            payment_data.get_address(),
            payment_data.get_recurring_details(),
            payment_data.get_currency(),
        );

        let routable_connector_choice = self.0.clone();

        let backend_input = payments_routing::make_dsl_input(&payments_dsl_input)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to construct dsl input")?;

        let connectors = payments_routing::perform_cgraph_filtering(
            state,
            key_store,
            routable_connector_choice,
            backend_input,
            None,
            profile_id,
            &common_enums::TransactionType::Payment,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Eligibility analysis failed for routable connectors")?;

        let connector_data = connectors
            .into_iter()
            .map(|conn| {
                api::ConnectorData::get_connector_by_name(
                    &state.conf.connectors,
                    &conn.connector.to_string(),
                    api::GetToken::Connector,
                    conn.merchant_connector_id.clone(),
                )
            })
            .collect::<CustomResult<Vec<_>, _>>()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Invalid connector name received")?;

        Ok(connector_data)
    }
}

pub async fn migrate_rules_for_profile(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    query_params: routing_types::RuleMigrationQuery,
) -> RouterResult<routing_types::RuleMigrationResult> {
    use api_models::routing::StaticRoutingAlgorithm as EuclidAlgorithm;

    let profile_id = query_params.profile_id.clone();
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();
    let merchant_key_store = merchant_context.get_merchant_key_store();
    let merchant_id = merchant_context.get_merchant_account().get_id();

    let business_profile = core_utils::validate_and_get_business_profile(
        db,
        key_manager_state,
        merchant_key_store,
        Some(&profile_id),
        merchant_id,
    )
    .await?
    .get_required_value("Profile")
    .change_context(errors::ApiErrorResponse::ProfileNotFound {
        id: profile_id.get_string_repr().to_owned(),
    })?;

    #[cfg(feature = "v1")]
    let active_payment_routing_ids: Vec<Option<common_utils::id_type::RoutingId>> = vec![
        business_profile
            .get_payment_routing_algorithm()
            .attach_printable("Failed to get payment routing algorithm")?
            .unwrap_or_default()
            .algorithm_id,
        business_profile
            .get_payout_routing_algorithm()
            .attach_printable("Failed to get payout routing algorithm")?
            .unwrap_or_default()
            .algorithm_id,
    ];

    #[cfg(feature = "v2")]
    let active_payment_routing_ids = [business_profile.routing_algorithm_id.clone()];

    let routing_metadatas = state
        .store
        .list_routing_algorithm_metadata_by_profile_id(
            &profile_id,
            i64::from(query_params.validated_limit()),
            i64::from(query_params.offset.unwrap_or_default()),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;

    let mut response_list = Vec::new();
    let mut error_list = Vec::new();

    let mut push_error = |algorithm_id, msg: String| {
        error_list.push(RuleMigrationError {
            profile_id: profile_id.clone(),
            algorithm_id,
            error: msg,
        });
    };

    for routing_metadata in routing_metadatas {
        let algorithm_id = routing_metadata.algorithm_id.clone();
        let algorithm = match db
            .find_routing_algorithm_by_profile_id_algorithm_id(&profile_id, &algorithm_id)
            .await
        {
            Ok(algo) => algo,
            Err(e) => {
                router_env::logger::error!(?e, ?algorithm_id, "Failed to fetch routing algorithm");
                push_error(algorithm_id, format!("Fetch error: {e:?}"));
                continue;
            }
        };

        let parsed_result = algorithm
            .algorithm_data
            .parse_value::<EuclidAlgorithm>("EuclidAlgorithm");

        let maybe_static_algorithm: Option<StaticRoutingAlgorithm> = match parsed_result {
            Ok(EuclidAlgorithm::Advanced(program)) => match program.try_into() {
                Ok(ip) => Some(StaticRoutingAlgorithm::Advanced(ip)),
                Err(e) => {
                    router_env::logger::error!(
                        ?e,
                        ?algorithm_id,
                        "Failed to convert advanced program"
                    );
                    push_error(algorithm_id.clone(), format!("Conversion error: {e:?}"));
                    None
                }
            },
            Ok(EuclidAlgorithm::Single(conn)) => {
                Some(StaticRoutingAlgorithm::Single(Box::new(conn.into())))
            }
            Ok(EuclidAlgorithm::Priority(connectors)) => Some(StaticRoutingAlgorithm::Priority(
                connectors.into_iter().map(Into::into).collect(),
            )),
            Ok(EuclidAlgorithm::VolumeSplit(splits)) => Some(StaticRoutingAlgorithm::VolumeSplit(
                splits.into_iter().map(Into::into).collect(),
            )),
            Ok(EuclidAlgorithm::ThreeDsDecisionRule(_)) => {
                router_env::logger::info!(
                    ?algorithm_id,
                    "Skipping 3DS rule migration (not supported yet)"
                );
                push_error(algorithm_id.clone(), "3DS migration not implemented".into());
                None
            }
            Err(e) => {
                router_env::logger::error!(?e, ?algorithm_id, "Failed to parse algorithm");
                push_error(algorithm_id.clone(), format!("Parse error: {e:?}"));
                None
            }
        };

        let Some(static_algorithm) = maybe_static_algorithm else {
            continue;
        };

        let routing_rule = RoutingRule {
            rule_id: Some(algorithm.algorithm_id.clone().get_string_repr().to_string()),
            name: algorithm.name.clone(),
            description: algorithm.description.clone(),
            created_by: profile_id.get_string_repr().to_string(),
            algorithm: static_algorithm,
            algorithm_for: algorithm.algorithm_for.into(),
            metadata: Some(RoutingMetadata {
                kind: algorithm.kind,
            }),
        };

        match create_de_euclid_routing_algo(&state, &routing_rule).await {
            Ok(decision_engine_routing_id) => {
                let mut is_active_rule = false;
                if active_payment_routing_ids.contains(&Some(algorithm.algorithm_id.clone())) {
                    link_de_euclid_routing_algorithm(
                        &state,
                        ActivateRoutingConfigRequest {
                            created_by: profile_id.get_string_repr().to_string(),
                            routing_algorithm_id: decision_engine_routing_id.clone(),
                        },
                    )
                    .await
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("unable to link active routing algorithm")?;
                    is_active_rule = true;
                }
                response_list.push(RuleMigrationResponse {
                    profile_id: profile_id.clone(),
                    euclid_algorithm_id: algorithm.algorithm_id.clone(),
                    decision_engine_algorithm_id: decision_engine_routing_id,
                    is_active_rule,
                });
            }
            Err(err) => {
                router_env::logger::error!(
                    decision_engine_rule_migration_error = ?err,
                    algorithm_id = ?algorithm.algorithm_id,
                    "Failed to insert into decision engine"
                );
                push_error(
                    algorithm.algorithm_id.clone(),
                    format!("Insertion error: {err:?}"),
                );
            }
        }
    }

    Ok(routing_types::RuleMigrationResult {
        success: response_list,
        errors: error_list,
    })
}

pub async fn decide_gateway_open_router(
    state: SessionState,
    req_body: OpenRouterDecideGatewayRequest,
) -> RouterResponse<DecideGatewayResponse> {
    let response = if state.conf.open_router.dynamic_routing_enabled {
        SRApiClient::send_decision_engine_request(
            &state,
            Method::Post,
            "decide-gateway",
            Some(req_body),
            None,
            None,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)?
        .response
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to perform decide gateway call with open router")?
    } else {
        Err(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Dynamic routing is not enabled")?
    };

    Ok(hyperswitch_domain_models::api::ApplicationResponse::Json(
        response,
    ))
}

pub async fn update_gateway_score_open_router(
    state: SessionState,
    req_body: UpdateScorePayload,
) -> RouterResponse<UpdateScoreResponse> {
    let response = if state.conf.open_router.dynamic_routing_enabled {
        SRApiClient::send_decision_engine_request(
            &state,
            Method::Post,
            "update-gateway-score",
            Some(req_body),
            None,
            None,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)?
        .response
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to perform update gateway score call with open router")?
    } else {
        Err(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Dynamic routing is not enabled")?
    };

    Ok(hyperswitch_domain_models::api::ApplicationResponse::Json(
        response,
    ))
}


// Function: get_max_auto_retries_enabled
// File: crates/common_utils/src/id_type/merchant.rs
// Module: common_utils

// Documentation: get_max_auto_retries_enabled key

pub fn get_max_auto_retries_enabled(&self) -> String


// Struct: PayPalIntegrationDone
// File: crates/api_models/src/connector_onboarding.rs
// Module: api_models
// Implementations: 0

pub struct PayPalIntegrationDone


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

pub fn new() -> &'static Self


// Struct: Settings
// File: crates/hyperswitch_connectors/src/connectors/multisafepay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Settings


// Function: get_mandate_type
// File: crates/router/src/core/mandate/helpers.rs
// Module: router

pub fn get_mandate_type(
    mandate_data: Option<api_payments::MandateData>,
    off_session: Option<bool>,
    setup_future_usage: Option<enums::FutureUsage>,
    customer_acceptance: Option<common_payments_types::CustomerAcceptance>,
    token: Option<String>,
    payment_method: Option<enums::PaymentMethod>,
) -> CustomResult<Option<api::MandateTransactionType>, errors::ValidationError>


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

impl api::PaymentSync for for Fiserv


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

// Documentation: Generate a test payment id with prefix test_

pub fn generate_test_payment_id_for_sample_data() -> Self


// Function: from_data
// File: crates/common_utils/src/types/keymanager.rs
// Module: common_utils

pub fn from_data(data: StrongSecret<Vec<u8>>) -> Self


// Function: get_connector_name_as_string
// File: crates/hyperswitch_domain_models/src/merchant_connector_account.rs
// Module: hyperswitch_domain_models

pub fn get_connector_name_as_string(&self) -> String


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

pub struct Datatrans

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


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

pub mod transformers;

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

use self::coinbase::CoinbaseWebhookDetails;
use crate::{
    constants::headers,
    types::ResponseRouterData,
    utils::{self, convert_amount},
};

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

impl Coinbase {
    pub fn new() -> &'static Self {
        &Self {
            amount_convertor: &StringMajorUnitForConnector,
        }
    }
}

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

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

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

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

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

    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let auth: coinbase::CoinbaseAuthType = coinbase::CoinbaseAuthType::try_from(auth_type)
            .change_context(errors::ConnectorError::FailedToObtainAuthType)?;
        Ok(vec![(
            headers::X_CC_API_KEY.to_string(),
            auth.api_key.into_masked(),
        )])
    }

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

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

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.error.error_type,
            message: response.error.message,
            reason: response.error.code,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}

impl ConnectorValidation for Coinbase {}

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

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

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

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

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

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

    fn get_url(
        &self,
        _req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!("{}/charges", self.base_url(_connectors)))
    }

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

        let connector_router_data = coinbase::CoinbaseRouterData::from((amount, req));
        let connector_req = coinbase::CoinbasePaymentsRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

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

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

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

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

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

    fn get_url(
        &self,
        _req: &PaymentsSyncRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let connector_id = _req
            .request
            .connector_transaction_id
            .get_connector_transaction_id()
            .change_context(errors::ConnectorError::MissingConnectorTransactionID)?;
        Ok(format!(
            "{}/charges/{}",
            self.base_url(_connectors),
            connector_id
        ))
    }

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

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

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

impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> for Coinbase {
    fn build_request(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::FlowNotSupported {
            flow: "Capture".to_string(),
            connector: "Coinbase".to_string(),
        }
        .into())
    }
}

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

impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Coinbase {
    fn build_request(
        &self,
        _req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::FlowNotSupported {
            flow: "Refund".to_string(),
            connector: "Coinbase".to_string(),
        }
        .into())
    }
}

impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for Coinbase {
    // default implementation of build_request method will be executed
}

#[async_trait::async_trait]
impl webhooks::IncomingWebhook for Coinbase {
    fn get_webhook_source_verification_algorithm(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn crypto::VerifySignature + Send>, errors::ConnectorError> {
        Ok(Box::new(crypto::HmacSha256))
    }

    fn get_webhook_source_verification_signature(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let base64_signature =
            utils::get_header_key_value("X-CC-Webhook-Signature", request.headers)?;
        hex::decode(base64_signature)
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)
    }

    fn get_webhook_source_verification_message(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let message = std::str::from_utf8(request.body)
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
        Ok(message.to_string().into_bytes())
    }

    fn get_webhook_object_reference_id(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        let notif: CoinbaseWebhookDetails = request
            .body
            .parse_struct("CoinbaseWebhookDetails")
            .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
        Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
            api_models::payments::PaymentIdType::ConnectorTransactionId(notif.event.data.id),
        ))
    }

    fn get_webhook_event_type(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
        let notif: CoinbaseWebhookDetails = request
            .body
            .parse_struct("CoinbaseWebhookDetails")
            .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
        match notif.event.event_type {
            coinbase::WebhookEventType::Confirmed | coinbase::WebhookEventType::Resolved => {
                Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentSuccess)
            }
            coinbase::WebhookEventType::Failed => {
                Ok(api_models::webhooks::IncomingWebhookEvent::PaymentActionRequired)
            }
            coinbase::WebhookEventType::Pending => {
                Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentProcessing)
            }
            coinbase::WebhookEventType::Unknown | coinbase::WebhookEventType::Created => {
                Ok(api_models::webhooks::IncomingWebhookEvent::EventNotSupported)
            }
        }
    }

    fn get_webhook_resource_object(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        let notif: CoinbaseWebhookDetails = request
            .body
            .parse_struct("CoinbaseWebhookDetails")
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;

        Ok(Box::new(notif.event))
    }
}

lazy_static! {
    static ref COINBASE_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
        display_name: "Coinbase",
        description:
            "Coinbase is a place for people and businesses to buy, sell, and manage crypto.",
        connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
        integration_status: enums::ConnectorIntegrationStatus::Beta,
    };
    static ref COINBASE_SUPPORTED_PAYMENT_METHODS: SupportedPaymentMethods = {
        let supported_capture_methods = vec![
            enums::CaptureMethod::Automatic,
            enums::CaptureMethod::Manual,
            enums::CaptureMethod::SequentialAutomatic,
        ];

        let mut coinbase_supported_payment_methods = SupportedPaymentMethods::new();

        coinbase_supported_payment_methods.add(
            enums::PaymentMethod::Crypto,
            enums::PaymentMethodType::CryptoCurrency,
            PaymentMethodDetails {
                mandates: common_enums::FeatureStatus::NotSupported,
                refunds: common_enums::FeatureStatus::NotSupported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: None,
            },
        );

        coinbase_supported_payment_methods
    };
    static ref COINBASE_SUPPORTED_WEBHOOK_FLOWS: Vec<enums::EventClass> =
        vec![enums::EventClass::Payments, enums::EventClass::Refunds,];
}

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

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

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


// Function: get_filters_for_payouts
// File: crates/diesel_models/src/query/payout_attempt.rs
// Module: diesel_models

pub fn get_filters_for_payouts(
        conn: &PgPooledConn,
        payouts: &[Payouts],
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> StorageResult<(
        Vec<String>,
        Vec<enums::Currency>,
        Vec<enums::PayoutStatus>,
        Vec<enums::PayoutType>,
    )>


// File: crates/api_models/src/webhook_events.rs
// Module: api_models
// Public functions: 1
// Public structs: 9

use std::collections::HashSet;

use common_enums::{EventClass, EventType, WebhookDeliveryAttempt};
use masking::Secret;
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;
use utoipa::ToSchema;

/// The constraints to apply when filtering events.
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
pub struct EventListConstraints {
    /// Filter events created after the specified time.
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub created_after: Option<PrimitiveDateTime>,

    /// Filter events created before the specified time.
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub created_before: Option<PrimitiveDateTime>,

    /// Include at most the specified number of events.
    pub limit: Option<u16>,

    /// Include events after the specified offset.
    pub offset: Option<u16>,

    /// Filter all events associated with the specified object identifier (Payment Intent ID,
    /// Refund ID, etc.)
    pub object_id: Option<String>,

    /// Filter all events associated with the specified business profile ID.
    #[schema(value_type = Option<String>)]
    pub profile_id: Option<common_utils::id_type::ProfileId>,

    /// Filter events by their class.
    pub event_classes: Option<HashSet<EventClass>>,

    /// Filter events by their type.
    pub event_types: Option<HashSet<EventType>>,
    /// Filter all events by `is_overall_delivery_successful` field of the event.
    pub is_delivered: Option<bool>,
}

#[derive(Debug)]
pub enum EventListConstraintsInternal {
    GenericFilter {
        created_after: Option<PrimitiveDateTime>,
        created_before: Option<PrimitiveDateTime>,
        limit: Option<i64>,
        offset: Option<i64>,
        event_classes: Option<HashSet<EventClass>>,
        event_types: Option<HashSet<EventType>>,
        is_delivered: Option<bool>,
    },
    ObjectIdFilter {
        object_id: String,
    },
}

/// The response body for each item when listing events.
#[derive(Debug, Serialize, ToSchema)]
pub struct EventListItemResponse {
    /// The identifier for the Event.
    #[schema(max_length = 64, example = "evt_018e31720d1b7a2b82677d3032cab959")]
    pub event_id: String,

    /// The identifier for the Merchant Account.
    #[schema(max_length = 64, example = "y3oqhf46pyzuxjbcn2giaqnb44", value_type = String)]
    pub merchant_id: common_utils::id_type::MerchantId,

    /// The identifier for the Business Profile.
    #[schema(max_length = 64, value_type = String, example = "SqB0zwDGR5wHppWf0bx7GKr1f2")]
    pub profile_id: common_utils::id_type::ProfileId,

    /// The identifier for the object (Payment Intent ID, Refund ID, etc.)
    #[schema(max_length = 64, example = "QHrfd5LUDdZaKtAjdJmMu0dMa1")]
    pub object_id: String,

    /// Specifies the type of event, which includes the object and its status.
    pub event_type: EventType,

    /// Specifies the class of event (the type of object: Payment, Refund, etc.)
    pub event_class: EventClass,

    /// Indicates whether the webhook was ultimately delivered or not.
    pub is_delivery_successful: Option<bool>,

    /// The identifier for the initial delivery attempt. This will be the same as `event_id` for
    /// the initial delivery attempt.
    #[schema(max_length = 64, example = "evt_018e31720d1b7a2b82677d3032cab959")]
    pub initial_attempt_id: String,

    /// Time at which the event was created.
    #[schema(example = "2022-09-10T10:11:12Z")]
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created: PrimitiveDateTime,
}

/// The response body of list initial delivery attempts api call.
#[derive(Debug, Serialize, ToSchema)]
pub struct TotalEventsResponse {
    /// The list of events
    pub events: Vec<EventListItemResponse>,
    /// Count of total events
    pub total_count: i64,
}

impl TotalEventsResponse {
    pub fn new(total_count: i64, events: Vec<EventListItemResponse>) -> Self {
        Self {
            events,
            total_count,
        }
    }
}

impl common_utils::events::ApiEventMetric for TotalEventsResponse {
    fn get_api_event_type(&self) -> Option<common_utils::events::ApiEventsType> {
        Some(common_utils::events::ApiEventsType::Events {
            merchant_id: self.events.first().map(|event| event.merchant_id.clone())?,
        })
    }
}

/// The response body for retrieving an event.
#[derive(Debug, Serialize, ToSchema)]
pub struct EventRetrieveResponse {
    #[serde(flatten)]
    pub event_information: EventListItemResponse,

    /// The request information (headers and body) sent in the webhook.
    pub request: OutgoingWebhookRequestContent,

    /// The response information (headers, body and status code) received for the webhook sent.
    pub response: OutgoingWebhookResponseContent,

    /// Indicates the type of delivery attempt.
    pub delivery_attempt: Option<WebhookDeliveryAttempt>,
}

impl common_utils::events::ApiEventMetric for EventRetrieveResponse {
    fn get_api_event_type(&self) -> Option<common_utils::events::ApiEventsType> {
        Some(common_utils::events::ApiEventsType::Events {
            merchant_id: self.event_information.merchant_id.clone(),
        })
    }
}

/// The request information (headers and body) sent in the webhook.
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct OutgoingWebhookRequestContent {
    /// The request body sent in the webhook.
    #[schema(value_type = String)]
    #[serde(alias = "payload")]
    pub body: Secret<String>,

    /// The request headers sent in the webhook.
    #[schema(
        value_type = Vec<(String, String)>,
        example = json!([["content-type", "application/json"], ["content-length", "1024"]]))
    ]
    pub headers: Vec<(String, Secret<String>)>,
}

/// The response information (headers, body and status code) received for the webhook sent.
#[derive(Debug, serde::Serialize, serde::Deserialize, ToSchema)]
pub struct OutgoingWebhookResponseContent {
    /// The response body received for the webhook sent.
    #[schema(value_type = Option<String>)]
    #[serde(alias = "payload")]
    pub body: Option<Secret<String>>,

    /// The response headers received for the webhook sent.
    #[schema(
        value_type = Option<Vec<(String, String)>>,
        example = json!([["content-type", "application/json"], ["content-length", "1024"]]))
    ]
    pub headers: Option<Vec<(String, Secret<String>)>>,

    /// The HTTP status code for the webhook sent.
    #[schema(example = 200)]
    pub status_code: Option<u16>,

    /// Error message in case any error occurred when trying to deliver the webhook.
    #[schema(example = 200)]
    pub error_message: Option<String>,
}

#[derive(Debug, serde::Serialize)]
pub struct EventListRequestInternal {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub constraints: EventListConstraints,
}

impl common_utils::events::ApiEventMetric for EventListRequestInternal {
    fn get_api_event_type(&self) -> Option<common_utils::events::ApiEventsType> {
        Some(common_utils::events::ApiEventsType::Events {
            merchant_id: self.merchant_id.clone(),
        })
    }
}

#[derive(Debug, serde::Serialize)]
pub struct WebhookDeliveryAttemptListRequestInternal {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub initial_attempt_id: String,
}

impl common_utils::events::ApiEventMetric for WebhookDeliveryAttemptListRequestInternal {
    fn get_api_event_type(&self) -> Option<common_utils::events::ApiEventsType> {
        Some(common_utils::events::ApiEventsType::Events {
            merchant_id: self.merchant_id.clone(),
        })
    }
}

#[derive(Debug, serde::Serialize)]
pub struct WebhookDeliveryRetryRequestInternal {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub event_id: String,
}

impl common_utils::events::ApiEventMetric for WebhookDeliveryRetryRequestInternal {
    fn get_api_event_type(&self) -> Option<common_utils::events::ApiEventsType> {
        Some(common_utils::events::ApiEventsType::Events {
            merchant_id: self.merchant_id.clone(),
        })
    }
}


// Struct: MerchantConnectorAccount
// File: crates/router/src/routes/app.rs
// Module: router
// Implementations: 2

pub struct MerchantConnectorAccount


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

impl super::KafkaMessage for for KafkaDispute


// Function: disable_decision_engine_dynamic_routing_setup
// File: crates/router/src/core/routing/helpers.rs
// Module: router

pub fn disable_decision_engine_dynamic_routing_setup(
    state: &SessionState,
    profile_id: &id_type::ProfileId,
    dynamic_routing_type: routing_types::DynamicRoutingType,
    dynamic_routing_algo_ref: &mut routing_types::DynamicRoutingAlgorithmRef,
) -> RouterResult<()>


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

pub struct CardVerification


// Function: payments_attempt_operation_core
// File: crates/router/src/core/payments.rs
// Module: router

pub fn payments_attempt_operation_core<F, Req, Op, D>(
    state: &SessionState,
    req_state: ReqState,
    merchant_context: domain::MerchantContext,
    profile: domain::Profile,
    operation: Op,
    req: Req,
    payment_id: id_type::GlobalPaymentId,
    header_payload: HeaderPayload,
) -> RouterResult<(D, Req, Option<domain::Customer>)>
where
    F: Send + Clone + Sync,
    Req: Clone,
    Op: Operation<F, Req, Data = D> + Send + Sync,
    D: OperationSessionGetters<F> + Send + Sync + Clone,


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

pub struct VaultConnectorFlowData


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

pub use api_models::payouts::{
    AchBankTransfer, BacsBankTransfer, Bank as BankPayout, CardPayout, PayoutActionRequest,
    PayoutAttemptResponse, PayoutCreateRequest, PayoutCreateResponse, PayoutListConstraints,
    PayoutListFilterConstraints, PayoutListFilters, PayoutListResponse, PayoutMethodData,
    PayoutRequest, PayoutRetrieveBody, PayoutRetrieveRequest, PixBankTransfer, SepaBankTransfer,
    Wallet as WalletPayout,
};
pub use hyperswitch_domain_models::router_flow_types::payouts::{
    PoCancel, PoCreate, PoEligibility, PoFulfill, PoQuote, PoRecipient, PoRecipientAccount, PoSync,
};
pub use hyperswitch_interfaces::api::payouts_v2::{
    PayoutCancelV2, PayoutCreateV2, PayoutEligibilityV2, PayoutFulfillV2, PayoutQuoteV2,
    PayoutRecipientAccountV2, PayoutRecipientV2, PayoutSyncV2,
};

use crate::types::api as api_types;

pub trait PayoutsV2:
    api_types::ConnectorCommon
    + PayoutCancelV2
    + PayoutCreateV2
    + PayoutEligibilityV2
    + PayoutFulfillV2
    + PayoutQuoteV2
    + PayoutRecipientV2
    + PayoutSyncV2
    + PayoutRecipientAccountV2
{
}


// Struct: HelcimPaymentsRequest
// File: crates/hyperswitch_connectors/src/connectors/helcim/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct HelcimPaymentsRequest


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

pub struct Tesouro

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


// Implementation: impl IncomingWebhook for for Prophetpay
// File: crates/hyperswitch_connectors/src/connectors/prophetpay.rs
// Module: hyperswitch_connectors
// Methods: 3 total (0 public)

impl IncomingWebhook for for Prophetpay


// Implementation: impl api::MandateSetup for for Santander
// File: crates/hyperswitch_connectors/src/connectors/santander.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::MandateSetup for for Santander


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

pub fn verify_recovery_code(
    state: SessionState,
    user_token: auth::UserIdFromAuth,
    req: user_api::VerifyRecoveryCodeRequest,
) -> UserResponse<()>


// Function: collect
// File: crates/analytics/src/refunds/accumulator.rs
// Module: analytics

pub fn collect(self) -> RefundMetricsBucketValue


// Function: merchant_account_kv_status
// File: crates/router/src/routes/admin.rs
// Module: router

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


// Trait: CustomerAddress
// File: crates/router/src/utils.rs
// Module: router

pub trait CustomerAddress


// Module Structure
// File: crates/router/src/core/relay.rs
// Module: router

// Public submodules:
pub mod utils;


// Struct: DwollaFundingSourceResponse
// File: crates/hyperswitch_connectors/src/connectors/dwolla/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct DwollaFundingSourceResponse


// Trait: IntoDirValue
// File: crates/kgraph_utils/src/transformers.rs
// Module: kgraph_utils

pub trait IntoDirValue


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

impl webhooks::IncomingWebhook for for Moneris


// Struct: NetceteraErrorResponse
// File: crates/hyperswitch_connectors/src/connectors/netcetera/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct NetceteraErrorResponse


// Function: from_headers
// File: crates/router/src/services/authentication/detached.rs
// Module: router

pub fn from_headers(headers: &HeaderMap) -> RouterResult<Self>


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

impl api::Refund for for Vgs


// Trait: AppStateInfo
// File: crates/router/src/routes/app.rs
// Module: router

pub trait AppStateInfo


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

impl api::RefundExecute for for Airwallex


// Implementation: impl ConnectorRedirectResponse for for Paybox
// File: crates/hyperswitch_connectors/src/connectors/paybox.rs
// Module: hyperswitch_connectors
// Methods: 1 total (0 public)

impl ConnectorRedirectResponse for for Paybox


// File: crates/router/src/db/kafka_store.rs
// Module: router
// Public functions: 1
// Public structs: 2

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

use ::payment_methods::state::PaymentMethodsStorageInterface;
use common_enums::enums::MerchantStorageScheme;
use common_utils::{
    errors::CustomResult,
    id_type,
    types::{keymanager::KeyManagerState, user::ThemeLineage},
};
#[cfg(feature = "v2")]
use diesel_models::ephemeral_key::{ClientSecretType, ClientSecretTypeNew};
use diesel_models::{
    enums::{self, ProcessTrackerStatus},
    ephemeral_key::{EphemeralKey, EphemeralKeyNew},
    refund as diesel_refund,
    reverse_lookup::{ReverseLookup, ReverseLookupNew},
    user_role as user_storage,
};
#[cfg(feature = "payouts")]
use hyperswitch_domain_models::payouts::{
    payout_attempt::PayoutAttemptInterface, payouts::PayoutsInterface,
};
use hyperswitch_domain_models::{
    cards_info::CardsInfoInterface,
    disputes,
    payment_methods::PaymentMethodInterface,
    payments::{payment_attempt::PaymentAttemptInterface, payment_intent::PaymentIntentInterface},
    refunds,
};
#[cfg(not(feature = "payouts"))]
use hyperswitch_domain_models::{PayoutAttemptInterface, PayoutsInterface};
use masking::Secret;
use redis_interface::{errors::RedisError, RedisConnectionPool, RedisEntryId};
use router_env::{instrument, logger, tracing};
use scheduler::{
    db::{process_tracker::ProcessTrackerInterface, queue::QueueInterface},
    SchedulerInterface,
};
use serde::Serialize;
use storage_impl::{config::TenantConfig, redis::kv_store::RedisConnInterface};
use time::PrimitiveDateTime;

use super::{
    dashboard_metadata::DashboardMetadataInterface,
    ephemeral_key::ClientSecretInterface,
    hyperswitch_ai_interaction::HyperswitchAiInteractionInterface,
    role::RoleInterface,
    user::{sample_data::BatchSampleDataInterface, theme::ThemeInterface, UserInterface},
    user_authentication_method::UserAuthenticationMethodInterface,
    user_key_store::UserKeyStoreInterface,
    user_role::{ListUserRolesByOrgIdPayload, ListUserRolesByUserIdPayload, UserRoleInterface},
};
#[cfg(feature = "payouts")]
use crate::services::kafka::payout::KafkaPayout;
use crate::{
    core::errors::{self, ProcessTrackerError},
    db::{
        self,
        address::AddressInterface,
        api_keys::ApiKeyInterface,
        authentication::AuthenticationInterface,
        authorization::AuthorizationInterface,
        business_profile::ProfileInterface,
        callback_mapper::CallbackMapperInterface,
        capture::CaptureInterface,
        configs::ConfigInterface,
        customers::CustomerInterface,
        dispute::DisputeInterface,
        ephemeral_key::EphemeralKeyInterface,
        events::EventInterface,
        file::FileMetadataInterface,
        generic_link::GenericLinkInterface,
        gsm::GsmInterface,
        health_check::HealthCheckDbInterface,
        locker_mock_up::LockerMockUpInterface,
        mandate::MandateInterface,
        merchant_account::MerchantAccountInterface,
        merchant_connector_account::{ConnectorAccessToken, MerchantConnectorAccountInterface},
        merchant_key_store::MerchantKeyStoreInterface,
        payment_link::PaymentLinkInterface,
        refund::RefundInterface,
        reverse_lookup::ReverseLookupInterface,
        routing_algorithm::RoutingAlgorithmInterface,
        tokenization::TokenizationInterface,
        unified_translations::UnifiedTranslationsInterface,
        AccountsStorageInterface, CommonStorageInterface, GlobalStorageInterface,
        MasterKeyInterface, StorageInterface,
    },
    services::{kafka::KafkaProducer, Store},
    types::{domain, storage, AccessToken},
};
#[derive(Debug, Clone, Serialize)]
pub struct TenantID(pub String);

#[derive(Clone)]
pub struct KafkaStore {
    pub kafka_producer: KafkaProducer,
    pub diesel_store: Store,
    pub tenant_id: TenantID,
}

impl KafkaStore {
    pub async fn new(
        store: Store,
        mut kafka_producer: KafkaProducer,
        tenant_id: TenantID,
        tenant_config: &dyn TenantConfig,
    ) -> Self {
        kafka_producer.set_tenancy(tenant_config);
        Self {
            kafka_producer,
            diesel_store: store,
            tenant_id,
        }
    }
}

#[async_trait::async_trait]
impl AddressInterface for KafkaStore {
    async fn find_address_by_address_id(
        &self,
        state: &KeyManagerState,
        address_id: &str,
        key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::Address, errors::StorageError> {
        self.diesel_store
            .find_address_by_address_id(state, address_id, key_store)
            .await
    }

    async fn update_address(
        &self,
        state: &KeyManagerState,
        address_id: String,
        address: storage::AddressUpdate,
        key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::Address, errors::StorageError> {
        self.diesel_store
            .update_address(state, address_id, address, key_store)
            .await
    }

    async fn update_address_for_payments(
        &self,
        state: &KeyManagerState,
        this: domain::PaymentAddress,
        address: domain::AddressUpdate,
        payment_id: id_type::PaymentId,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<domain::PaymentAddress, errors::StorageError> {
        self.diesel_store
            .update_address_for_payments(
                state,
                this,
                address,
                payment_id,
                key_store,
                storage_scheme,
            )
            .await
    }

    async fn insert_address_for_payments(
        &self,
        state: &KeyManagerState,
        payment_id: &id_type::PaymentId,
        address: domain::PaymentAddress,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<domain::PaymentAddress, errors::StorageError> {
        self.diesel_store
            .insert_address_for_payments(state, payment_id, address, key_store, storage_scheme)
            .await
    }

    async fn find_address_by_merchant_id_payment_id_address_id(
        &self,
        state: &KeyManagerState,
        merchant_id: &id_type::MerchantId,
        payment_id: &id_type::PaymentId,
        address_id: &str,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<domain::PaymentAddress, errors::StorageError> {
        self.diesel_store
            .find_address_by_merchant_id_payment_id_address_id(
                state,
                merchant_id,
                payment_id,
                address_id,
                key_store,
                storage_scheme,
            )
            .await
    }

    async fn insert_address_for_customers(
        &self,
        state: &KeyManagerState,
        address: domain::CustomerAddress,
        key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::Address, errors::StorageError> {
        self.diesel_store
            .insert_address_for_customers(state, address, key_store)
            .await
    }

    async fn update_address_by_merchant_id_customer_id(
        &self,
        state: &KeyManagerState,
        customer_id: &id_type::CustomerId,
        merchant_id: &id_type::MerchantId,
        address: storage::AddressUpdate,
        key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<Vec<domain::Address>, errors::StorageError> {
        self.diesel_store
            .update_address_by_merchant_id_customer_id(
                state,
                customer_id,
                merchant_id,
                address,
                key_store,
            )
            .await
    }
}

#[async_trait::async_trait]
impl ApiKeyInterface for KafkaStore {
    async fn insert_api_key(
        &self,
        api_key: storage::ApiKeyNew,
    ) -> CustomResult<storage::ApiKey, errors::StorageError> {
        self.diesel_store.insert_api_key(api_key).await
    }

    async fn update_api_key(
        &self,
        merchant_id: id_type::MerchantId,
        key_id: id_type::ApiKeyId,
        api_key: storage::ApiKeyUpdate,
    ) -> CustomResult<storage::ApiKey, errors::StorageError> {
        self.diesel_store
            .update_api_key(merchant_id, key_id, api_key)
            .await
    }

    async fn revoke_api_key(
        &self,
        merchant_id: &id_type::MerchantId,
        key_id: &id_type::ApiKeyId,
    ) -> CustomResult<bool, errors::StorageError> {
        self.diesel_store.revoke_api_key(merchant_id, key_id).await
    }

    async fn find_api_key_by_merchant_id_key_id_optional(
        &self,
        merchant_id: &id_type::MerchantId,
        key_id: &id_type::ApiKeyId,
    ) -> CustomResult<Option<storage::ApiKey>, errors::StorageError> {
        self.diesel_store
            .find_api_key_by_merchant_id_key_id_optional(merchant_id, key_id)
            .await
    }

    async fn find_api_key_by_hash_optional(
        &self,
        hashed_api_key: storage::HashedApiKey,
    ) -> CustomResult<Option<storage::ApiKey>, errors::StorageError> {
        self.diesel_store
            .find_api_key_by_hash_optional(hashed_api_key)
            .await
    }

    async fn list_api_keys_by_merchant_id(
        &self,
        merchant_id: &id_type::MerchantId,
        limit: Option<i64>,
        offset: Option<i64>,
    ) -> CustomResult<Vec<storage::ApiKey>, errors::StorageError> {
        self.diesel_store
            .list_api_keys_by_merchant_id(merchant_id, limit, offset)
            .await
    }
}

#[async_trait::async_trait]
impl CardsInfoInterface for KafkaStore {
    type Error = errors::StorageError;
    async fn get_card_info(
        &self,
        card_iin: &str,
    ) -> CustomResult<Option<storage::CardInfo>, errors::StorageError> {
        self.diesel_store.get_card_info(card_iin).await
    }

    async fn add_card_info(
        &self,
        data: storage::CardInfo,
    ) -> CustomResult<storage::CardInfo, errors::StorageError> {
        self.diesel_store.add_card_info(data).await
    }

    async fn update_card_info(
        &self,
        card_iin: String,
        data: storage::UpdateCardInfo,
    ) -> CustomResult<storage::CardInfo, errors::StorageError> {
        self.diesel_store.update_card_info(card_iin, data).await
    }
}

#[async_trait::async_trait]
impl ConfigInterface for KafkaStore {
    async fn insert_config(
        &self,
        config: storage::ConfigNew,
    ) -> CustomResult<storage::Config, errors::StorageError> {
        self.diesel_store.insert_config(config).await
    }

    async fn find_config_by_key(
        &self,
        key: &str,
    ) -> CustomResult<storage::Config, errors::StorageError> {
        self.diesel_store.find_config_by_key(key).await
    }

    async fn find_config_by_key_from_db(
        &self,
        key: &str,
    ) -> CustomResult<storage::Config, errors::StorageError> {
        self.diesel_store.find_config_by_key_from_db(key).await
    }

    async fn update_config_in_database(
        &self,
        key: &str,
        config_update: storage::ConfigUpdate,
    ) -> CustomResult<storage::Config, errors::StorageError> {
        self.diesel_store
            .update_config_in_database(key, config_update)
            .await
    }

    async fn update_config_by_key(
        &self,
        key: &str,
        config_update: storage::ConfigUpdate,
    ) -> CustomResult<storage::Config, errors::StorageError> {
        self.diesel_store
            .update_config_by_key(key, config_update)
            .await
    }

    async fn delete_config_by_key(
        &self,
        key: &str,
    ) -> CustomResult<storage::Config, errors::StorageError> {
        self.diesel_store.delete_config_by_key(key).await
    }

    async fn find_config_by_key_unwrap_or(
        &self,
        key: &str,
        default_config: Option<String>,
    ) -> CustomResult<storage::Config, errors::StorageError> {
        self.diesel_store
            .find_config_by_key_unwrap_or(key, default_config)
            .await
    }
}

#[async_trait::async_trait]
impl CustomerInterface for KafkaStore {
    type Error = errors::StorageError;
    #[cfg(feature = "v1")]
    async fn delete_customer_by_customer_id_merchant_id(
        &self,
        customer_id: &id_type::CustomerId,
        merchant_id: &id_type::MerchantId,
    ) -> CustomResult<bool, errors::StorageError> {
        self.diesel_store
            .delete_customer_by_customer_id_merchant_id(customer_id, merchant_id)
            .await
    }

    #[cfg(feature = "v1")]
    async fn find_customer_optional_by_customer_id_merchant_id(
        &self,
        state: &KeyManagerState,
        customer_id: &id_type::CustomerId,
        merchant_id: &id_type::MerchantId,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<Option<domain::Customer>, errors::StorageError> {
        self.diesel_store
            .find_customer_optional_by_customer_id_merchant_id(
                state,
                customer_id,
                merchant_id,
                key_store,
                storage_scheme,
            )
            .await
    }

    #[cfg(feature = "v1")]
    async fn find_customer_optional_with_redacted_customer_details_by_customer_id_merchant_id(
        &self,
        state: &KeyManagerState,
        customer_id: &id_type::CustomerId,
        merchant_id: &id_type::MerchantId,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<Option<domain::Customer>, errors::StorageError> {
        self.diesel_store
            .find_customer_optional_with_redacted_customer_details_by_customer_id_merchant_id(
                state,
                customer_id,
                merchant_id,
                key_store,
                storage_scheme,
            )
            .await
    }

    #[cfg(feature = "v2")]
    async fn find_optional_by_merchant_id_merchant_reference_id(
        &self,
        state: &KeyManagerState,
        customer_id: &id_type::CustomerId,
        merchant_id: &id_type::MerchantId,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<Option<domain::Customer>, errors::StorageError> {
        self.diesel_store
            .find_optional_by_merchant_id_merchant_reference_id(
                state,
                customer_id,
                merchant_id,
                key_store,
                storage_scheme,
            )
            .await
    }

    #[cfg(feature = "v1")]
    async fn update_customer_by_customer_id_merchant_id(
        &self,
        state: &KeyManagerState,
        customer_id: id_type::CustomerId,
        merchant_id: id_type::MerchantId,
        customer: domain::Customer,
        customer_update: storage::CustomerUpdate,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<domain::Customer, errors::StorageError> {
        self.diesel_store
            .update_customer_by_customer_id_merchant_id(
                state,
                customer_id,
                merchant_id,
                customer,
                customer_update,
                key_store,
                storage_scheme,
            )
            .await
    }

    #[cfg(feature = "v2")]
    async fn update_customer_by_global_id(
        &self,
        state: &KeyManagerState,
        id: &id_type::GlobalCustomerId,
        customer: domain::Customer,
        customer_update: storage::CustomerUpdate,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<domain::Customer, errors::StorageError> {
        self.diesel_store
            .update_customer_by_global_id(
                state,
                id,
                customer,
                customer_update,
                key_store,
                storage_scheme,
            )
            .await
    }

    async fn list_customers_by_merchant_id(
        &self,
        state: &KeyManagerState,
        merchant_id: &id_type::MerchantId,
        key_store: &domain::MerchantKeyStore,
        constraints: super::customers::CustomerListConstraints,
    ) -> CustomResult<Vec<domain::Customer>, errors::StorageError> {
        self.diesel_store
            .list_customers_by_merchant_id(state, merchant_id, key_store, constraints)
            .await
    }

    #[cfg(feature = "v1")]
    async fn find_customer_by_customer_id_merchant_id(
        &self,
        state: &KeyManagerState,
        customer_id: &id_type::CustomerId,
        merchant_id: &id_type::MerchantId,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<domain::Customer, errors::StorageError> {
        self.diesel_store
            .find_customer_by_customer_id_merchant_id(
                state,
                customer_id,
                merchant_id,
                key_store,
                storage_scheme,
            )
            .await
    }

    #[cfg(feature = "v2")]
    async fn find_customer_by_merchant_reference_id_merchant_id(
        &self,
        state: &KeyManagerState,
        merchant_reference_id: &id_type::CustomerId,
        merchant_id: &id_type::MerchantId,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<domain::Customer, errors::StorageError> {
        self.diesel_store
            .find_customer_by_merchant_reference_id_merchant_id(
                state,
                merchant_reference_id,
                merchant_id,
                key_store,
                storage_scheme,
            )
            .await
    }

    #[cfg(feature = "v2")]
    async fn find_customer_by_global_id(
        &self,
        state: &KeyManagerState,
        id: &id_type::GlobalCustomerId,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<domain::Customer, errors::StorageError> {
        self.diesel_store
            .find_customer_by_global_id(state, id, key_store, storage_scheme)
            .await
    }

    async fn insert_customer(
        &self,
        customer_data: domain::Customer,
        state: &KeyManagerState,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<domain::Customer, errors::StorageError> {
        self.diesel_store
            .insert_customer(customer_data, state, key_store, storage_scheme)
            .await
    }
}

#[async_trait::async_trait]
impl DisputeInterface for KafkaStore {
    async fn insert_dispute(
        &self,
        dispute_new: storage::DisputeNew,
    ) -> CustomResult<storage::Dispute, errors::StorageError> {
        let dispute = self.diesel_store.insert_dispute(dispute_new).await?;

        if let Err(er) = self
            .kafka_producer
            .log_dispute(&dispute, None, self.tenant_id.clone())
            .await
        {
            logger::error!(message="Failed to add analytics entry for Dispute {dispute:?}", error_message=?er);
        };

        Ok(dispute)
    }

    async fn find_by_merchant_id_payment_id_connector_dispute_id(
        &self,
        merchant_id: &id_type::MerchantId,
        payment_id: &id_type::PaymentId,
        connector_dispute_id: &str,
    ) -> CustomResult<Option<storage::Dispute>, errors::StorageError> {
        self.diesel_store
            .find_by_merchant_id_payment_id_connector_dispute_id(
                merchant_id,
                payment_id,
                connector_dispute_id,
            )
            .await
    }

    async fn find_dispute_by_merchant_id_dispute_id(
        &self,
        merchant_id: &id_type::MerchantId,
        dispute_id: &str,
    ) -> CustomResult<storage::Dispute, errors::StorageError> {
        self.diesel_store
            .find_dispute_by_merchant_id_dispute_id(merchant_id, dispute_id)
            .await
    }

    async fn find_disputes_by_constraints(
        &self,
        merchant_id: &id_type::MerchantId,
        dispute_constraints: &disputes::DisputeListConstraints,
    ) -> CustomResult<Vec<storage::Dispute>, errors::StorageError> {
        self.diesel_store
            .find_disputes_by_constraints(merchant_id, dispute_constraints)
            .await
    }

    async fn update_dispute(
        &self,
        this: storage::Dispute,
        dispute: storage::DisputeUpdate,
    ) -> CustomResult<storage::Dispute, errors::StorageError> {
        let dispute_new = self
            .diesel_store
            .update_dispute(this.clone(), dispute)
            .await?;
        if let Err(er) = self
            .kafka_producer
            .log_dispute(&dispute_new, Some(this), self.tenant_id.clone())
            .await
        {
            logger::error!(message="Failed to add analytics entry for Dispute {dispute_new:?}", error_message=?er);
        };

        Ok(dispute_new)
    }

    async fn find_disputes_by_merchant_id_payment_id(
        &self,
        merchant_id: &id_type::MerchantId,
        payment_id: &id_type::PaymentId,
    ) -> CustomResult<Vec<storage::Dispute>, errors::StorageError> {
        self.diesel_store
            .find_disputes_by_merchant_id_payment_id(merchant_id, payment_id)
            .await
    }

    async fn get_dispute_status_with_count(
        &self,
        merchant_id: &id_type::MerchantId,
        profile_id_list: Option<Vec<id_type::ProfileId>>,
        time_range: &common_utils::types::TimeRange,
    ) -> CustomResult<Vec<(common_enums::DisputeStatus, i64)>, errors::StorageError> {
        self.diesel_store
            .get_dispute_status_with_count(merchant_id, profile_id_list, time_range)
            .await
    }
}

#[async_trait::async_trait]
impl EphemeralKeyInterface for KafkaStore {
    #[cfg(feature = "v1")]
    async fn create_ephemeral_key(
        &self,
        ek: EphemeralKeyNew,
        validity: i64,
    ) -> CustomResult<EphemeralKey, errors::StorageError> {
        self.diesel_store.create_ephemeral_key(ek, validity).await
    }

    #[cfg(feature = "v1")]
    async fn get_ephemeral_key(
        &self,
        key: &str,
    ) -> CustomResult<EphemeralKey, errors::StorageError> {
        self.diesel_store.get_ephemeral_key(key).await
    }

    #[cfg(feature = "v1")]
    async fn delete_ephemeral_key(
        &self,
        id: &str,
    ) -> CustomResult<EphemeralKey, errors::StorageError> {
        self.diesel_store.delete_ephemeral_key(id).await
    }
}

#[async_trait::async_trait]
impl ClientSecretInterface for KafkaStore {
    #[cfg(feature = "v2")]
    async fn create_client_secret(
        &self,
        ek: ClientSecretTypeNew,
        validity: i64,
    ) -> CustomResult<ClientSecretType, errors::StorageError> {
        self.diesel_store.create_client_secret(ek, validity).await
    }

    #[cfg(feature = "v2")]
    async fn get_client_secret(
        &self,
        key: &str,
    ) -> CustomResult<ClientSecretType, errors::StorageError> {
        self.diesel_store.get_client_secret(key).await
    }

    #[cfg(feature = "v2")]
    async fn delete_client_secret(
        &self,
        id: &str,
    ) -> CustomResult<ClientSecretType, errors::StorageError> {
        self.diesel_store.delete_client_secret(id).await
    }
}

#[async_trait::async_trait]
impl EventInterface for KafkaStore {
    async fn insert_event(
        &self,
        state: &KeyManagerState,
        event: domain::Event,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::Event, errors::StorageError> {
        self.diesel_store
            .insert_event(state, event, merchant_key_store)
            .await
    }

    async fn find_event_by_merchant_id_event_id(
        &self,
        state: &KeyManagerState,
        merchant_id: &id_type::MerchantId,
        event_id: &str,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::Event, errors::StorageError> {
        self.diesel_store
            .find_event_by_merchant_id_event_id(state, merchant_id, event_id, merchant_key_store)
            .await
    }

    async fn find_event_by_merchant_id_idempotent_event_id(
        &self,
        state: &KeyManagerState,
        merchant_id: &id_type::MerchantId,
        idempotent_event_id: &str,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::Event, errors::StorageError> {
        self.diesel_store
            .find_event_by_merchant_id_idempotent_event_id(
                state,
                merchant_id,
                idempotent_event_id,
                merchant_key_store,
            )
            .await
    }

    async fn list_initial_events_by_merchant_id_primary_object_id(
        &self,
        state: &KeyManagerState,
        merchant_id: &id_type::MerchantId,
        primary_object_id: &str,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<Vec<domain::Event>, errors::StorageError> {
        self.diesel_store
            .list_initial_events_by_merchant_id_primary_object_id(
                state,
                merchant_id,
                primary_object_id,
                merchant_key_store,
            )
            .await
    }

    async fn list_initial_events_by_merchant_id_constraints(
        &self,
        state: &KeyManagerState,
        merchant_id: &id_type::MerchantId,
        created_after: PrimitiveDateTime,
        created_before: PrimitiveDateTime,
        limit: Option<i64>,
        offset: Option<i64>,
        event_types: HashSet<common_enums::EventType>,
        is_delivered: Option<bool>,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<Vec<domain::Event>, errors::StorageError> {
        self.diesel_store
            .list_initial_events_by_merchant_id_constraints(
                state,
                merchant_id,
                created_after,
                created_before,
                limit,
                offset,
                event_types,
                is_delivered,
                merchant_key_store,
            )
            .await
    }

    async fn list_events_by_merchant_id_initial_attempt_id(
        &self,
        state: &KeyManagerState,
        merchant_id: &id_type::MerchantId,
        initial_attempt_id: &str,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<Vec<domain::Event>, errors::StorageError> {
        self.diesel_store
            .list_events_by_merchant_id_initial_attempt_id(
                state,
                merchant_id,
                initial_attempt_id,
                merchant_key_store,
            )
            .await
    }

    async fn list_initial_events_by_profile_id_primary_object_id(
        &self,
        state: &KeyManagerState,
        profile_id: &id_type::ProfileId,
        primary_object_id: &str,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<Vec<domain::Event>, errors::StorageError> {
        self.diesel_store
            .list_initial_events_by_profile_id_primary_object_id(
                state,
                profile_id,
                primary_object_id,
                merchant_key_store,
            )
            .await
    }

    async fn list_initial_events_by_profile_id_constraints(
        &self,
        state: &KeyManagerState,
        profile_id: &id_type::ProfileId,
        created_after: PrimitiveDateTime,
        created_before: PrimitiveDateTime,
        limit: Option<i64>,
        offset: Option<i64>,
        event_types: HashSet<common_enums::EventType>,
        is_delivered: Option<bool>,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<Vec<domain::Event>, errors::StorageError> {
        self.diesel_store
            .list_initial_events_by_profile_id_constraints(
                state,
                profile_id,
                created_after,
                created_before,
                limit,
                offset,
                event_types,
                is_delivered,
                merchant_key_store,
            )
            .await
    }

    async fn update_event_by_merchant_id_event_id(
        &self,
        state: &KeyManagerState,
        merchant_id: &id_type::MerchantId,
        event_id: &str,
        event: domain::EventUpdate,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::Event, errors::StorageError> {
        self.diesel_store
            .update_event_by_merchant_id_event_id(
                state,
                merchant_id,
                event_id,
                event,
                merchant_key_store,
            )
            .await
    }

    async fn count_initial_events_by_constraints(
        &self,
        merchant_id: &id_type::MerchantId,
        profile_id: Option<id_type::ProfileId>,
        created_after: PrimitiveDateTime,
        created_before: PrimitiveDateTime,
        event_types: HashSet<common_enums::EventType>,
        is_delivered: Option<bool>,
    ) -> CustomResult<i64, errors::StorageError> {
        self.diesel_store
            .count_initial_events_by_constraints(
                merchant_id,
                profile_id,
                created_after,
                created_before,
                event_types,
                is_delivered,
            )
            .await
    }
}

#[async_trait::async_trait]
impl LockerMockUpInterface for KafkaStore {
    async fn find_locker_by_card_id(
        &self,
        card_id: &str,
    ) -> CustomResult<storage::LockerMockUp, errors::StorageError> {
        self.diesel_store.find_locker_by_card_id(card_id).await
    }

    async fn insert_locker_mock_up(
        &self,
        new: storage::LockerMockUpNew,
    ) -> CustomResult<storage::LockerMockUp, errors::StorageError> {
        self.diesel_store.insert_locker_mock_up(new).await
    }

    async fn delete_locker_mock_up(
        &self,
        card_id: &str,
    ) -> CustomResult<storage::LockerMockUp, errors::StorageError> {
        self.diesel_store.delete_locker_mock_up(card_id).await
    }
}

#[async_trait::async_trait]
impl MandateInterface for KafkaStore {
    async fn find_mandate_by_merchant_id_mandate_id(
        &self,
        merchant_id: &id_type::MerchantId,
        mandate_id: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage::Mandate, errors::StorageError> {
        self.diesel_store
            .find_mandate_by_merchant_id_mandate_id(merchant_id, mandate_id, storage_scheme)
            .await
    }

    async fn find_mandate_by_merchant_id_connector_mandate_id(
        &self,
        merchant_id: &id_type::MerchantId,
        connector_mandate_id: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage::Mandate, errors::StorageError> {
        self.diesel_store
            .find_mandate_by_merchant_id_connector_mandate_id(
                merchant_id,
                connector_mandate_id,
                storage_scheme,
            )
            .await
    }

    #[cfg(feature = "v2")]
    async fn find_mandate_by_global_customer_id(
        &self,
        id: &id_type::GlobalCustomerId,
    ) -> CustomResult<Vec<storage::Mandate>, errors::StorageError> {
        self.diesel_store
            .find_mandate_by_global_customer_id(id)
            .await
    }

    async fn find_mandate_by_merchant_id_customer_id(
        &self,
        merchant_id: &id_type::MerchantId,
        customer_id: &id_type::CustomerId,
    ) -> CustomResult<Vec<storage::Mandate>, errors::StorageError> {
        self.diesel_store
            .find_mandate_by_merchant_id_customer_id(merchant_id, customer_id)
            .await
    }

    async fn update_mandate_by_merchant_id_mandate_id(
        &self,
        merchant_id: &id_type::MerchantId,
        mandate_id: &str,
        mandate_update: storage::MandateUpdate,
        mandate: storage::Mandate,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage::Mandate, errors::StorageError> {
        self.diesel_store
            .update_mandate_by_merchant_id_mandate_id(
                merchant_id,
                mandate_id,
                mandate_update,
                mandate,
                storage_scheme,
            )
            .await
    }

    async fn find_mandates_by_merchant_id(
        &self,
        merchant_id: &id_type::MerchantId,
        mandate_constraints: api_models::mandates::MandateListConstraints,
    ) -> CustomResult<Vec<storage::Mandate>, errors::StorageError> {
        self.diesel_store
            .find_mandates_by_merchant_id(merchant_id, mandate_constraints)
            .await
    }

    async fn insert_mandate(
        &self,
        mandate: storage::MandateNew,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage::Mandate, errors::StorageError> {
        self.diesel_store
            .insert_mandate(mandate, storage_scheme)
            .await
    }
}

#[async_trait::async_trait]
impl PaymentLinkInterface for KafkaStore {
    async fn find_payment_link_by_payment_link_id(
        &self,
        payment_link_id: &str,
    ) -> CustomResult<storage::PaymentLink, errors::StorageError> {
        self.diesel_store
            .find_payment_link_by_payment_link_id(payment_link_id)
            .await
    }

    async fn insert_payment_link(
        &self,
        payment_link_object: storage::PaymentLinkNew,
    ) -> CustomResult<storage::PaymentLink, errors::StorageError> {
        self.diesel_store
            .insert_payment_link(payment_link_object)
            .await
    }

    async fn list_payment_link_by_merchant_id(
        &self,
        merchant_id: &id_type::MerchantId,
        payment_link_constraints: api_models::payments::PaymentLinkListConstraints,
    ) -> CustomResult<Vec<storage::PaymentLink>, errors::StorageError> {
        self.diesel_store
            .list_payment_link_by_merchant_id(merchant_id, payment_link_constraints)
            .await
    }
}

#[async_trait::async_trait]
impl MerchantAccountInterface for KafkaStore {
    async fn insert_merchant(
        &self,
        state: &KeyManagerState,
        merchant_account: domain::MerchantAccount,
        key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantAccount, errors::StorageError> {
        self.diesel_store
            .insert_merchant(state, merchant_account, key_store)
            .await
    }

    async fn find_merchant_account_by_merchant_id(
        &self,
        state: &KeyManagerState,
        merchant_id: &id_type::MerchantId,
        key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantAccount, errors::StorageError> {
        self.diesel_store
            .find_merchant_account_by_merchant_id(state, merchant_id, key_store)
            .await
    }

    async fn update_merchant(
        &self,
        state: &KeyManagerState,
        this: domain::MerchantAccount,
        merchant_account: storage::MerchantAccountUpdate,
        key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantAccount, errors::StorageError> {
        self.diesel_store
            .update_merchant(state, this, merchant_account, key_store)
            .await
    }

    async fn update_specific_fields_in_merchant(
        &self,
        state: &KeyManagerState,
        merchant_id: &id_type::MerchantId,
        merchant_account: storage::MerchantAccountUpdate,
        key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantAccount, errors::StorageError> {
        self.diesel_store
            .update_specific_fields_in_merchant(state, merchant_id, merchant_account, key_store)
            .await
    }

    async fn update_all_merchant_account(
        &self,
        merchant_account: storage::MerchantAccountUpdate,
    ) -> CustomResult<usize, errors::StorageError> {
        self.diesel_store
            .update_all_merchant_account(merchant_account)
            .await
    }