Repository: juspay/hyperswitch
File: crates/router/src/core/payments/helpers.rs (Rust)

Code before:
}

/// Returns `true` if either the org or merchant config is set to "true"
pub async fn is_merchant_eligible_authentication_service(
    merchant_id: &id_type::MerchantId,
    org_id: &id_type::OrganizationId,
    state: &SessionState,
) -> RouterResult<bool> {
    let db = &*state.store;
    let org_key = org_id.get_authentication_service_eligible_key();
    let merchant_key = merchant_id.get_authentication_service_eligible_key();
    let org_eligible = db
        .find_config_by_key(&org_key)
        .await
        .inspect_err(|error| {
            logger::error!(?error, "Failed to fetch `{org_key}` config from DB");
        })
        .is_ok_and(|c| c.config.to_lowercase() == "true");

    let merchant_eligible = db
        .find_config_by_key(&merchant_key)
        .await
        .inspect_err(|error| {
            logger::error!(?error, "Failed to fetch `{merchant_key}` config from DB");
        })
        .is_ok_and(|c| c.config.to_lowercase() == "true");

    Ok(org_eligible || merchant_eligible)
}

#[cfg(feature = "v1")]
pub async fn validate_allowed_payment_method_types_request(
    state: &SessionState,
    profile_id: &id_type::ProfileId,
    processor: &domain::Processor,
    allowed_payment_method_types: Option<Vec<common_enums::PaymentMethodType>>,
) -> CustomResult<(), errors::ApiErrorResponse> {
    if let Some(allowed_payment_method_types) = allowed_payment_method_types {
        let db = &*state.store;
        let all_connector_accounts = db
            .find_merchant_connector_account_by_merchant_id_and_disabled_list(
                processor.get_account().get_id(),
                false,
                processor.get_key_store(),
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to fetch merchant connector account for given merchant id")?;

        let filtered_connector_accounts = all_connector_accounts
            .filter_based_on_profile_and_connector_type(

Review comment (performance):
we need not fetch `merchant_eligible` if we get `org_eligible` as true. Can you please optimize this.

Code after:
}

/// Returns `true` if either the org or merchant config is set to "true"
///
/// Priority logic:
/// 1. If org-level config exists (either "true" or "false"), that decision is final
///    - Org = "true" → returns true (authentication enabled)
///    - Org = "false" → returns false (authentication disabled, merchant config ignored)
/// 2. If org-level config is missing or fails to fetch, fallback to merchant-level config
///    - Merchant = "true" → returns true
///    - Merchant = "false" or missing → returns false
///
/// This ensures parent (org) rules take precedence over child (merchant) configurations
pub async fn is_merchant_eligible_authentication_service(
    merchant_id: &id_type::MerchantId,
    org_id: &id_type::OrganizationId,
    state: &SessionState,
) -> RouterResult<bool> {
    let db = &*state.store;
    let org_key = org_id.get_authentication_service_eligible_key();
    let org_eligible = db
        .find_config_by_key(&org_key)
        .await
        .inspect_err(|error| {
            logger::error!(?error, "Failed to fetch `{org_key}` config from DB");
        })
        .ok()
        .map(|c| c.config.to_lowercase() == "true");

    Ok(org_eligible
        .async_unwrap_or_else(|| async {
            let merchant_key = merchant_id.get_authentication_service_eligible_key();
            db.find_config_by_key(&merchant_key)
                .await
                .inspect_err(|error| {
                    logger::error!(?error, "Failed to fetch `{merchant_key}` config from DB");
                })
                .ok()
                .map(|c| c.config.to_lowercase() == "true")
                .unwrap_or(false)
        })
        .await)
}

#[cfg(feature = "v1")]
pub async fn validate_allowed_payment_method_types_request(
    state: &SessionState,
    profile_id: &id_type::ProfileId,
    processor: &domain::Processor,
    allowed_payment_method_types: Option<Vec<common_enums::PaymentMethodType>>,
) -> CustomResult<(), errors::ApiErrorResponse> {