Bug: filter debit networks based on merchant connector account configuration



Once the open router call is completed for the co-badged card lookup in a debit routing flow, it returns all the debit networks eligible for the card. These networks must be validated against the merchant-enabled debit networks for each merchant connector account.


diff --git a/crates/router/src/core/debit_routing.rs b/crates/router/src/core/debit_routing.rs
index adbd663c5c1..a929c146cec 100644
--- a/crates/router/src/core/debit_routing.rs
+++ b/crates/router/src/core/debit_routing.rs
@@ -2,7 +2,9 @@ use std::{collections::HashSet, fmt::Debug};
 
 use api_models::{enums as api_enums, open_router};
 use common_enums::enums;
-use common_utils::id_type;
+use common_utils::{
+    errors::CustomResult, ext_traits::ValueExt, id_type, types::keymanager::KeyManagerState,
+};
 use error_stack::ResultExt;
 use masking::Secret;
 
@@ -22,6 +24,7 @@ use crate::{
         api::{self, ConnectorCallType},
         domain,
     },
+    utils::id_type::MerchantConnectorAccountId,
 };
 
 pub struct DebitRoutingResult {
@@ -65,7 +68,6 @@ where
                     logger::info!("Performing debit routing for PreDetermined connector");
                     handle_pre_determined_connector(
                         state,
-                        &debit_routing_config,
                         debit_routing_supported_connectors,
                         &connector_data,
                         payment_data,
@@ -77,7 +79,6 @@ where
                     logger::info!("Performing debit routing for Retryable connector");
                     handle_retryable_connector(
                         state,
-                        &debit_routing_config,
                         debit_routing_supported_connectors,
                         connector_data,
                         payment_data,
@@ -234,7 +235,6 @@ pub async fn check_for_debit_routing_connector_in_profile<
 
 async fn handle_pre_determined_connector<F, D>(
     state: &SessionState,
-    debit_routing_config: &settings::DebitRoutingConfig,
     debit_routing_supported_connectors: HashSet<api_enums::Connector>,
     connector_data: &api::ConnectorRoutingData,
     payment_data: &mut D,
@@ -244,6 +244,11 @@ where
     F: Send + Clone,
     D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
 {
+    let db = state.store.as_ref();
+    let key_manager_state = &(state).into();
+    let merchant_id = payment_data.get_payment_attempt().merchant_id.clone();
+    let profile_id = payment_data.get_payment_attempt().profile_id.clone();
+
     if debit_routing_supported_connectors.contains(&connector_data.connector_data.connector_name) {
         logger::debug!("Chosen connector is supported for debit routing");
 
@@ -255,15 +260,43 @@ where
             debit_routing_output.co_badged_card_networks
         );
 
-        let valid_connectors = build_connector_routing_data(
-            connector_data,
-            debit_routing_config,
-            &debit_routing_output.co_badged_card_networks,
-        );
+        let key_store = db
+            .get_merchant_key_store_by_merchant_id(
+                key_manager_state,
+                &merchant_id,
+                &db.get_master_key().to_vec().into(),
+            )
+            .await
+            .change_context(errors::ApiErrorResponse::MerchantAccountNotFound)
+            .map_err(|error| {
+                logger::error!(
+                    "Failed to get merchant key store by merchant_id  {:?}",
+                    error
+                )
+            })
+            .ok()?;
+
+        let connector_routing_data = build_connector_routing_data(
+            state,
+            &profile_id,
+            &key_store,
+            vec![connector_data.clone()],
+            debit_routing_output.co_badged_card_networks.clone(),
+        )
+        .await
+        .map_err(|error| {
+            logger::error!(
+                "Failed to build connector routing data for debit routing {:?}",
+                error
+            )
+        })
+        .ok()?;
 
-        if !valid_connectors.is_empty() {
+        if !connector_routing_data.is_empty() {
             return Some(DebitRoutingResult {
-                debit_routing_connector_call_type: ConnectorCallType::Retryable(valid_connectors),
+                debit_routing_connector_call_type: ConnectorCallType::Retryable(
+                    connector_routing_data,
+                ),
                 debit_routing_output,
             });
         }
@@ -377,44 +410,8 @@ where
     }
 }
 
-fn check_connector_support_for_network(
-    debit_routing_config: &settings::DebitRoutingConfig,
-    connector_name: api_enums::Connector,
-    network: &enums::CardNetwork,
-) -> Option<enums::CardNetwork> {
-    debit_routing_config
-        .connector_supported_debit_networks
-        .get(&connector_name)
-        .and_then(|supported_networks| {
-            (supported_networks.contains(network) || network.is_global_network())
-                .then(|| network.clone())
-        })
-}
-
-fn build_connector_routing_data(
-    connector_data: &api::ConnectorRoutingData,
-    debit_routing_config: &settings::DebitRoutingConfig,
-    fee_sorted_debit_networks: &[enums::CardNetwork],
-) -> Vec<api::ConnectorRoutingData> {
-    fee_sorted_debit_networks
-        .iter()
-        .filter_map(|network| {
-            check_connector_support_for_network(
-                debit_routing_config,
-                connector_data.connector_data.connector_name,
-                network,
-            )
-            .map(|valid_network| api::ConnectorRoutingData {
-                connector_data: connector_data.connector_data.clone(),
-                network: Some(valid_network),
-            })
-        })
-        .collect()
-}
-
 async fn handle_retryable_connector<F, D>(
     state: &SessionState,
-    debit_routing_config: &settings::DebitRoutingConfig,
     debit_routing_supported_connectors: HashSet<api_enums::Connector>,
     connector_data_list: Vec<api::ConnectorRoutingData>,
     payment_data: &mut D,
@@ -424,6 +421,10 @@ where
     F: Send + Clone,
     D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
 {
+    let key_manager_state = &(state).into();
+    let db = state.store.as_ref();
+    let profile_id = payment_data.get_payment_attempt().profile_id.clone();
+    let merchant_id = payment_data.get_payment_attempt().merchant_id.clone();
     let is_any_debit_routing_connector_supported =
         connector_data_list.iter().any(|connector_data| {
             debit_routing_supported_connectors
@@ -433,32 +434,212 @@ where
     if is_any_debit_routing_connector_supported {
         let debit_routing_output =
             get_debit_routing_output::<F, D>(state, payment_data, acquirer_country).await?;
-
-        logger::debug!(
-            "Sorted co-badged networks: {:?}",
-            debit_routing_output.co_badged_card_networks
-        );
-
-        let supported_connectors: Vec<_> = connector_data_list
-            .iter()
-            .flat_map(|connector_data| {
-                build_connector_routing_data(
-                    connector_data,
-                    debit_routing_config,
-                    &debit_routing_output.co_badged_card_networks,
+        let key_store = db
+            .get_merchant_key_store_by_merchant_id(
+                key_manager_state,
+                &merchant_id,
+                &db.get_master_key().to_vec().into(),
+            )
+            .await
+            .change_context(errors::ApiErrorResponse::MerchantAccountNotFound)
+            .map_err(|error| {
+                logger::error!(
+                    "Failed to get merchant key store by merchant_id  {:?}",
+                    error
                 )
             })
-            .collect();
+            .ok()?;
+
+        let connector_routing_data = build_connector_routing_data(
+            state,
+            &profile_id,
+            &key_store,
+            connector_data_list.clone(),
+            debit_routing_output.co_badged_card_networks.clone(),
+        )
+        .await
+        .map_err(|error| {
+            logger::error!(
+                "Failed to build connector routing data for debit routing {:?}",
+                error
+            )
+        })
+        .ok()?;
 
-        if !supported_connectors.is_empty() {
+        if !connector_routing_data.is_empty() {
             return Some(DebitRoutingResult {
                 debit_routing_connector_call_type: ConnectorCallType::Retryable(
-                    supported_connectors,
+                    connector_routing_data,
                 ),
                 debit_routing_output,
             });
-        }
+        };
     }
 
     None
 }
+
+async fn build_connector_routing_data(
+    state: &SessionState,
+    profile_id: &id_type::ProfileId,
+    key_store: &domain::MerchantKeyStore,
+    eligible_connector_data_list: Vec<api::ConnectorRoutingData>,
+    fee_sorted_debit_networks: Vec<common_enums::CardNetwork>,
+) -> CustomResult<Vec<api::ConnectorRoutingData>, errors::ApiErrorResponse> {
+    let key_manager_state = &state.into();
+    let debit_routing_config = &state.conf.debit_routing_config;
+
+    let mcas_for_profile =
+        fetch_merchant_connector_accounts(state, key_manager_state, profile_id, key_store).await?;
+
+    let mut connector_routing_data = Vec::new();
+    let mut has_us_local_network = false;
+
+    for connector_data in eligible_connector_data_list {
+        if let Some(routing_data) = process_connector_for_networks(
+            &connector_data,
+            &mcas_for_profile,
+            &fee_sorted_debit_networks,
+            debit_routing_config,
+            &mut has_us_local_network,
+        )? {
+            connector_routing_data.extend(routing_data);
+        }
+    }
+
+    validate_us_local_network_requirement(has_us_local_network)?;
+    Ok(connector_routing_data)
+}
+
+/// Fetches merchant connector accounts for the given profile
+async fn fetch_merchant_connector_accounts(
+    state: &SessionState,
+    key_manager_state: &KeyManagerState,
+    profile_id: &id_type::ProfileId,
+    key_store: &domain::MerchantKeyStore,
+) -> CustomResult<Vec<domain::MerchantConnectorAccount>, errors::ApiErrorResponse> {
+    state
+        .store
+        .list_enabled_connector_accounts_by_profile_id(
+            key_manager_state,
+            profile_id,
+            key_store,
+            common_enums::ConnectorType::PaymentProcessor,
+        )
+        .await
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Failed to fetch merchant connector accounts")
+}
+
+/// Processes a single connector to find matching networks
+fn process_connector_for_networks(
+    connector_data: &api::ConnectorRoutingData,
+    mcas_for_profile: &[domain::MerchantConnectorAccount],
+    fee_sorted_debit_networks: &[common_enums::CardNetwork],
+    debit_routing_config: &settings::DebitRoutingConfig,
+    has_us_local_network: &mut bool,
+) -> CustomResult<Option<Vec<api::ConnectorRoutingData>>, errors::ApiErrorResponse> {
+    let Some(merchant_connector_id) = &connector_data.connector_data.merchant_connector_id else {
+        logger::warn!("Skipping connector with missing merchant_connector_id");
+        return Ok(None);
+    };
+
+    let Some(account) = find_merchant_connector_account(mcas_for_profile, merchant_connector_id)
+    else {
+        logger::warn!(
+            "No MCA found for merchant_connector_id: {:?}",
+            merchant_connector_id
+        );
+        return Ok(None);
+    };
+
+    let merchant_debit_networks = extract_debit_networks(&account)?;
+    let matching_networks = find_matching_networks(
+        &merchant_debit_networks,
+        fee_sorted_debit_networks,
+        &connector_data.connector_data,
+        debit_routing_config,
+        has_us_local_network,
+    );
+
+    Ok(Some(matching_networks))
+}
+
+/// Finds a merchant connector account by ID
+fn find_merchant_connector_account(
+    mcas: &[domain::MerchantConnectorAccount],
+    merchant_connector_id: &MerchantConnectorAccountId,
+) -> Option<domain::MerchantConnectorAccount> {
+    mcas.iter()
+        .find(|mca| mca.merchant_connector_id == *merchant_connector_id)
+        .cloned()
+}
+
+/// Finds networks that match between merchant and fee-sorted networks
+fn find_matching_networks(
+    merchant_debit_networks: &HashSet<common_enums::CardNetwork>,
+    fee_sorted_debit_networks: &[common_enums::CardNetwork],
+    connector_data: &api::ConnectorData,
+    debit_routing_config: &settings::DebitRoutingConfig,
+    has_us_local_network: &mut bool,
+) -> Vec<api::ConnectorRoutingData> {
+    let is_routing_enabled = debit_routing_config
+        .supported_connectors
+        .contains(&connector_data.connector_name);
+
+    fee_sorted_debit_networks
+        .iter()
+        .filter(|network| merchant_debit_networks.contains(network))
+        .filter(|network| is_routing_enabled || network.is_global_network())
+        .map(|network| {
+            if network.is_us_local_network() {
+                *has_us_local_network = true;
+            }
+
+            api::ConnectorRoutingData {
+                connector_data: connector_data.clone(),
+                network: Some(network.clone()),
+            }
+        })
+        .collect()
+}
+
+/// Validates that at least one US local network is present
+fn validate_us_local_network_requirement(
+    has_us_local_network: bool,
+) -> CustomResult<(), errors::ApiErrorResponse> {
+    if !has_us_local_network {
+        return Err(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("At least one US local network is required in routing");
+    }
+    Ok(())
+}
+
+fn extract_debit_networks(
+    account: &domain::MerchantConnectorAccount,
+) -> CustomResult<HashSet<common_enums::CardNetwork>, errors::ApiErrorResponse> {
+    let mut networks = HashSet::new();
+
+    if let Some(values) = &account.payment_methods_enabled {
+        for val in values {
+            let payment_methods_enabled: api_models::admin::PaymentMethodsEnabled =
+                val.to_owned().parse_value("PaymentMethodsEnabled")
+                    .change_context(errors::ApiErrorResponse::InternalServerError)
+                    .attach_printable("Failed to parse enabled payment methods for a merchant connector account in debit routing flow")?;
+
+            if let Some(types) = payment_methods_enabled.payment_method_types {
+                for method_type in types {
+                    if method_type.payment_method_type
+                        == api_models::enums::PaymentMethodType::Debit
+                    {
+                        if let Some(card_networks) = method_type.card_networks {
+                            networks.extend(card_networks);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    Ok(networks)
+}

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [ ] New feature
- [ ] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
<!-- Describe your changes in detail -->
Once the open router call is completed for the co-badged card lookup in a debit routing flow, it returns all the debit networks eligible for the card. These networks must be validated against the merchant-enabled debit networks for each merchant connector account.

This PR adds the necessary filtering logic to enforce this validation.

### Additional Changes

- [ ] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!--
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->


## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->
-> Create a profile and enable debit routing
```
curl --location 'http://localhost:8080/account/merchant_1748851578/business_profile/pro_gEwS6ve9KVqywWL0hmAq' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: ' \
--data '{
    "is_debit_routing_enabled": true
}'
```
```
{
    "merchant_id": "merchant_1748851578",
    "profile_id": "pro_gEwS6ve9KVqywWL0hmAq",
    "profile_name": "US_default",
    "return_url": "https://google.com/success",
    "enable_payment_response_hash": true,
    "payment_response_hash_key": "ksFwEBEmzqH60K5XvD60le6J3S742RIcTLKHyZF3O8TwniOm7ldxztuZvFet7Zpb",
    "redirect_to_merchant_with_http_post": false,
    "webhook_details": {
        "webhook_version": "1.0.1",
        "webhook_username": "ekart_retail",
        "webhook_password": "password_ekart@123",
        "webhook_url": null,
        "payment_created_enabled": true,
        "payment_succeeded_enabled": true,
        "payment_failed_enabled": true
    },
    "metadata": null,
    "routing_algorithm": null,
    "intent_fulfillment_time": 900,
    "frm_routing_algorithm": null,
    "payout_routing_algorithm": null,
    "applepay_verified_domains": null,
    "session_expiry": 900,
    "payment_link_config": null,
    "authentication_connector_details": null,
    "use_billing_as_payment_method_billing": true,
    "extended_card_info_config": null,
    "collect_shipping_details_from_wallet_connector": false,
    "collect_billing_details_from_wallet_connector": false,
    "always_collect_shipping_details_from_wallet_connector": false,
    "always_collect_billing_details_from_wallet_connector": false,
    "is_connector_agnostic_mit_enabled": false,
    "payout_link_config": null,
    "outgoing_webhook_custom_http_headers": null,
    "tax_connector_id": null,
    "is_tax_connector_enabled": false,
    "is_network_tokenization_enabled": false,
    "is_auto_retries_enabled": false,
    "max_auto_retries_enabled": null,
    "always_request_extended_authorization": null,
    "is_click_to_pay_enabled": false,
    "authentication_product_ids": null,
    "card_testing_guard_config": {
        "card_ip_blocking_status": "disabled",
        "card_ip_blocking_threshold": 3,
        "guest_user_card_blocking_status": "disabled",
        "guest_user_card_blocking_threshold": 10,
        "customer_id_blocking_status": "disabled",
        "customer_id_blocking_threshold": 5,
        "card_testing_guard_expiry": 3600
    },
    "is_clear_pan_retries_enabled": false,
    "force_3ds_challenge": false,
    "is_debit_routing_enabled": true,
    "merchant_business_country": null,
    "is_pre_network_tokenization_enabled": false
}
```
-> Create a mca with local debit networks enabled
```
{
        "payment_method_type": "debit",
        "card_networks": [
            "Nyce",
            "Star",
            "Accel"
        ],
        "minimum_amount": 1,
        "maximum_amount": 68607706,
        "recurring_enabled": true,
        "installment_payment_enabled": true
}
```
```
{
    "connector_type": "payment_processor",
    "connector_name": "adyen",
    "connector_label": "adyen_US_default",
    "merchant_connector_id": "mca_zMyPaKk9qUVauLL7wFrV",
    "profile_id": "pro_gEwS6ve9KVqywWL0hmAq",
    "connector_account_details": {
        "auth_type": "SignatureKey",
        "api_key": "AQ*************************************************************************************************************************************************7$",
        "key1": "Ju********OM",
        "api_secret": "AQ**********************************************************************************************************************************************************eb"
    },
    "payment_methods_enabled": [
        {
            "payment_method": "card",
            "payment_method_types": [
                {
                    "payment_method_type": "credit",
                    "payment_experience": null,
                    "card_networks": [
                        "Visa",
                        "Mastercard"
                    ],
                    "accepted_currencies": null,
                    "accepted_countries": null,
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                },
                {
                    "payment_method_type": "debit",
                    "payment_experience": null,
                    "card_networks": [
                        "Visa",
                        "Mastercard",
                        "Discover"
                    ],
                    "accepted_currencies": null,
                    "accepted_countries": null,
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                },
                {
                    "payment_method_type": "debit",
                    "payment_experience": null,
                    "card_networks": [
                        "Nyce",
                        "Star",
                        "Accel"
                    ],
                    "accepted_currencies": null,
                    "accepted_countries": null,
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                }
            ]
        },
        {
            "payment_method": "wallet",
            "payment_method_types": [
                {
                    "payment_method_type": "paypal",
                    "payment_experience": "redirect_to_url",
                    "card_networks": null,
                    "accepted_currencies": null,
                    "accepted_countries": null,
                    "minimum_amount": 0,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": false
                },
                {
                    "payment_method_type": "samsung_pay",
                    "payment_experience": "invoke_sdk_client",
                    "card_networks": null,
                    "accepted_currencies": null,
                    "accepted_countries": null,
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                },
                {
                    "payment_method_type": "apple_pay",
                    "payment_experience": "invoke_sdk_client",
                    "card_networks": null,
                    "accepted_currencies": null,
                    "accepted_countries": null,
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                }
            ]
        }
    ],
    "connector_webhook_details": null,
    "test_mode": false,
    "disabled": false,
    "frm_configs": null,
    "business_country": "US",
    "business_label": "default",
    "business_sub_label": null,
    "applepay_verified_domains": null,
    "pm_auth_config": null,
    "status": "active",
    "additional_merchant_data": null
}
```
-> Make a payment
```
curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'x-feature: router-custom' \
--header 'x-tenant-id: hyperswitch' \
--header 'api-key: ' \
--data-raw '{
    "amount": 10000,
    "amount_to_capture": 10000,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "setup_future_usage": "on_session",
    "capture_on": "2022-09-10T10:11:12Z",
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+1",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "customer_id": "cu_1748852614",
    "return_url": "http://127.0.0.1:4040",
    "payment_method": "card",
    "payment_method_type": "debit",
    "payment_method_data": {
        "card": {
            "card_number": "4000033003300335",
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": "joseph Doe",
            "card_cvc": "737"
        }
    },
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX",
            "last_name": "ss"
        },
        "email": "raj@gmail.com"
    },
    "shipping": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX"
        }
    },
    "customer_acceptance": {
        "acceptance_type": "offline",
        "accepted_at": "1963-05-03T04:07:52.723Z",
        "online": {
            "ip_address": "in sit",
            "user_agent": "amet irure esse"
        }
    },
    "browser_info": {
        "user_agent": "Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/70.0.3538.110 Safari\/537.36",
        "accept_header": "text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/webp,image\/apng,*\/*;q=0.8",
        "language": "nl-NL",
        "color_depth": 24,
        "screen_height": 723,
        "screen_width": 1536,
        "time_zone": 0,
        "java_enabled": true,
        "java_script_enabled": true,
        "ip_address": "125.0.0.1"
    },
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}'
```
```
{
    "payment_id": "pay_Q769H87jQ58yIcQC73sv",
    "merchant_id": "merchant_1748851578",
    "status": "succeeded",
    "amount": 10000,
    "net_amount": 10000,
    "shipping_cost": null,
    "amount_capturable": 0,
    "amount_received": 10000,
    "connector": "adyen",
    "client_secret": "pay_Q769H87jQ58yIcQC73sv_secret_WiHYRmir48iHRQKZ4lEl",
    "created": "2025-06-02T08:14:21.393Z",
    "currency": "USD",
    "customer_id": "cu_1748852061",
    "customer": {
        "id": "cu_1748852061",
        "name": "John Doe",
        "email": "guest@example.com",
        "phone": "999999999",
        "phone_country_code": "+1"
    },
    "description": "Its my first payment request",
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": "on_session",
    "off_session": null,
    "capture_on": null,
    "capture_method": "automatic",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "last4": "0335",
            "card_type": "DEBIT",
            "card_network": "Nyce",
            "card_issuer": "VISA U.S.A. INC.",
            "card_issuing_country": "UNITEDSTATES",
            "card_isin": "400003",
            "card_extended_bin": null,
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": "joseph Doe",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "PiX",
            "last_name": null
        },
        "phone": null,
        "email": null
    },
    "billing": {
        "address": {
            "city": "San",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "PiX",
            "last_name": "ss"
        },
        "phone": null,
        "email": "raj@gmail.com"
    },
    "order_details": null,
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "return_url": "http://127.0.0.1:4040/",
    "authentication_type": "no_three_ds",
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "next_action": null,
    "cancellation_reason": null,
    "error_code": null,
    "error_message": null,
    "unified_code": null,
    "unified_message": null,
    "payment_experience": null,
    "payment_method_type": "debit",
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": {
        "customer_id": "cu_1748852061",
        "created_at": 1748852061,
        "expires": 1748855661,
        "secret": "epk_da558f5301a24518abd1a5199f29b516"
    },
    "manual_retry_allowed": false,
    "connector_transaction_id": "LZ57WXZRDMJFMZ65",
    "frm_message": null,
    "metadata": {
        "udf1": "value1",
        "login_date": "2019-09-10T10:11:12Z",
        "new_customer": "true"
    },
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": "pay_Q769H87jQ58yIcQC73sv_1",
    "payment_link": null,
    "profile_id": "pro_gEwS6ve9KVqywWL0hmAq",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_zMyPaKk9qUVauLL7wFrV",
    "incremental_authorization_allowed": null,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2025-06-02T08:29:21.393Z",
    "fingerprint": null,
    "browser_info": {
        "language": "nl-NL",
        "time_zone": 0,
        "ip_address": "125.0.0.1",
        "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
        "color_depth": 24,
        "java_enabled": true,
        "screen_width": 1536,
        "accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "screen_height": 723,
        "java_script_enabled": true
    },
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2025-06-02T08:14:22.686Z",
    "split_payments": null,
    "frm_metadata": null,
    "extended_authorization_applied": null,
    "capture_before": null,
    "merchant_order_reference_id": null,
    "order_tax_amount": null,
    "connector_mandate_id": null,
    "card_discovery": "manual",
    "force_3ds_challenge": false,
    "force_3ds_challenge_trigger": false,
    "issuer_error_code": null,
    "issuer_error_message": null,
    "is_iframe_redirection_enabled": null,
    "whole_connector_response": null
}
```
For the above payment the eligible connector is visa, pulse and nyce. Since nyce is the cheapest network for the payment the payment went through it.
-> Now in order to test the filtering logic update the mca my removing nyce
```
{
        "payment_method_type": "debit",
        "card_networks": [
            "Star",
            "Accel"
        ],
        "minimum_amount": 1,
        "maximum_amount": 68607706,
        "recurring_enabled": true,
        "installment_payment_enabled": true
}
```
-> Make a payment
```
curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'x-feature: router-custom' \
--header 'x-tenant-id: hyperswitch' \
--header 'api-key: ' \
--data-raw '{
    "amount": 10000,
    "amount_to_capture": 10000,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "setup_future_usage": "on_session",
    "capture_on": "2022-09-10T10:11:12Z",
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+1",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "customer_id": "cu_1748852960",
    "return_url": "http://127.0.0.1:4040",
    "payment_method": "card",
    "payment_method_type": "debit",
    "payment_method_data": {
        "card": {
            "card_number": "4000033003300335",
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": "joseph Doe",
            "card_cvc": "737"
        }
    },
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX",
            "last_name": "ss"
        },
        "email": "raj@gmail.com"
    },
    "shipping": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX"
        }
    },
    "customer_acceptance": {
        "acceptance_type": "offline",
        "accepted_at": "1963-05-03T04:07:52.723Z",
        "online": {
            "ip_address": "in sit",
            "user_agent": "amet irure esse"
        }
    },
    "browser_info": {
        "user_agent": "Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/70.0.3538.110 Safari\/537.36",
        "accept_header": "text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/webp,image\/apng,*\/*;q=0.8",
        "language": "nl-NL",
        "color_depth": 24,
        "screen_height": 723,
        "screen_width": 1536,
        "time_zone": 0,
        "java_enabled": true,
        "java_script_enabled": true,
        "ip_address": "125.0.0.1"
    },
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}'
```
```
{
    "payment_id": "pay_wJSrmVAbpj6s9YpaH6oo",
    "merchant_id": "merchant_1748851578",
    "status": "succeeded",
    "amount": 10000,
    "net_amount": 10000,
    "shipping_cost": null,
    "amount_capturable": 0,
    "amount_received": 10000,
    "connector": "adyen",
    "client_secret": "pay_wJSrmVAbpj6s9YpaH6oo_secret_ddu0dC5yz2jePhmhRXwU",
    "created": "2025-06-02T08:29:13.189Z",
    "currency": "USD",
    "customer_id": "cu_1748852953",
    "customer": {
        "id": "cu_1748852953",
        "name": "John Doe",
        "email": "guest@example.com",
        "phone": "999999999",
        "phone_country_code": "+1"
    },
    "description": "Its my first payment request",
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": "on_session",
    "off_session": null,
    "capture_on": null,
    "capture_method": "automatic",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "last4": "0335",
            "card_type": "DEBIT",
            "card_network": "Visa",
            "card_issuer": "VISA U.S.A. INC.",
            "card_issuing_country": "UNITEDSTATES",
            "card_isin": "400003",
            "card_extended_bin": null,
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": "joseph Doe",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "PiX",
            "last_name": null
        },
        "phone": null,
        "email": null
    },
    "billing": {
        "address": {
            "city": "San",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "PiX",
            "last_name": "ss"
        },
        "phone": null,
        "email": "raj@gmail.com"
    },
    "order_details": null,
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "return_url": "http://127.0.0.1:4040/",
    "authentication_type": "no_three_ds",
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "next_action": null,
    "cancellation_reason": null,
    "error_code": null,
    "error_message": null,
    "unified_code": null,
    "unified_message": null,
    "payment_experience": null,
    "payment_method_type": "debit",
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": {
        "customer_id": "cu_1748852953",
        "created_at": 1748852953,
        "expires": 1748856553,
        "secret": "epk_d82b5b921478431fb6739e549f75a175"
    },
    "manual_retry_allowed": false,
    "connector_transaction_id": "ZPGN742WSN32HF75",
    "frm_message": null,
    "metadata": {
        "udf1": "value1",
        "login_date": "2019-09-10T10:11:12Z",
        "new_customer": "true"
    },
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": "pay_wJSrmVAbpj6s9YpaH6oo_1",
    "payment_link": null,
    "profile_id": "pro_gEwS6ve9KVqywWL0hmAq",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_zMyPaKk9qUVauLL7wFrV",
    "incremental_authorization_allowed": null,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2025-06-02T08:44:13.189Z",
    "fingerprint": null,
    "browser_info": {
        "language": "nl-NL",
        "time_zone": 0,
        "ip_address": "125.0.0.1",
        "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
        "color_depth": 24,
        "java_enabled": true,
        "screen_width": 1536,
        "accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "screen_height": 723,
        "java_script_enabled": true
    },
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2025-06-02T08:29:15.270Z",
    "split_payments": null,
    "frm_metadata": null,
    "extended_authorization_applied": null,
    "capture_before": null,
    "merchant_order_reference_id": null,
    "order_tax_amount": null,
    "connector_mandate_id": null,
    "card_discovery": "manual",
    "force_3ds_challenge": false,
    "force_3ds_challenge_trigger": false,
    "issuer_error_code": null,
    "issuer_error_message": null,
    "is_iframe_redirection_enabled": null,
    "whole_connector_response": null
}
```
Since nyce and pulse is not enabled for a mca payment was processed with visa.


## Checklist
<!-- Put an `x` in the boxes that apply -->

- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [x] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **Refactor**
  - Enhanced debit routing to be profile- and merchant-aware, improving connector and network selection accuracy.
  - Enforced US local network presence requirement for debit routing compliance.
  - Improved error handling and logging for better diagnostics.

- **Bug Fixes**
  - Resolved network selection issues by filtering connectors and networks based on merchant account eligibility.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->