Issue:
Bug: Create authentications to fallback to ApiKeyAuth if AdminApiAuth fails



Create two new authentication mechanisms that enhance the existing `AdminApiAuth` and `AdminApiAuthWithMerchantIdFromRoute` by allowing fallback to regular API key-based authentication when the admin key check fails.

This will be used by Merchant Account and API Key routes.

Pull request description:
## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [x] New feature
- [ ] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
<!-- Describe your changes in detail -->
This PR introduces two new authentication mechanisms that enhance the existing `AdminApiAuth` and `AdminApiAuthWithMerchantIdFromRoute` by allowing fallback to regular API key-based authentication when the admin key check fails.

### 1. `AdminApiAuthWithApiKeyFallback`

- Used in routes where **no merchant context** is required.
- Authenticates using:
  1. `admin_api_key`
  2. If that fails, falls back to:
     - Validating the provided `api-key`
     - Ensuring its associated `merchant_id` matches the configured `fallback_merchant_id` from the merchant_ids set.

### 2. `AdminApiAuthWithApiKeyFallbackWithMerchant`

- Used in routes that require **full merchant context** (`AuthenticationData`)
- Authenticates using:
  1. `admin_api_key` — on success, loads `AuthenticationData`
  2. If that fails:
     - Validates the provided `api-key`
     - Ensures:
       - The `merchant_id` from `api-key` also matches the configured `fallback_merchant_id` from the merchant_ids set.
     - If all conditions pass, loads and returns `AuthenticationData`
### 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).
-->
Some internal routes currently rely on `AdminApiAuth` using a shared `admin_api_key`. However, there are scenarios where we want to allow scoped access using an `api-key` associated with a specific merchant — **only if it matches a configured fallback merchant** from the merchant_ids set.


## 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)?
-->
The following APIs need to be validated with 
- AdminAPIKey
- the API Key associated with the configured merchant_id in the ENVs.

Tested by setting the ENV through
```bash
ROUTER__FALLBACK_MERCHANT_IDS_API_KEY_AUTH__MERCHANT_IDS="merchant_1744036688, merchant_1744037289" cargo run
```
And also tested by specifying the values in the TOML file.

#### Merchant Account - Create
```bash
curl --location 'http://localhost:8080/accounts' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_PY3zf8m1f0HjtqxO7QS36ZRSGMP5u9IWsmDki4fwq5iY0W8e6RoF4WMwCLvlojJM' \
--data-raw '{
  "merchant_id": "merchant_1744040286",
  "locker_id": "m0010",
  "merchant_name": "Nike",
  "merchant_details": {
    "primary_contact_person": "John Test",
    "primary_email": "JohnTest@test.com",
    "primary_phone": "sunt laborum",
    "secondary_contact_person": "John Test2",
    "secondary_email": "JohnTest2@test.com",
    "secondary_phone": "cillum do dolor id",
    "website": "https://www.example.com",
    "about_business": "Online Retail with a wide selection of organic products for North America",
    "address": {
      "line1": "1467",
      "line2": "Harrison Street",
      "line3": "Harrison Street",
      "city": "San Fransico",
      "state": "California",
      "zip": "94122",
      "country": "US",
      "first_name":"john",
      "last_name":"Doe"
    }
  },
  "return_url": "https://google.com/success",
  "webhook_details": {
    "webhook_version": "1.0.1",
    "webhook_username": "ekart_retail",
    "webhook_password": "password_ekart@123",
    "webhook_url":"https://webhook.site",
    "payment_created_enabled": true,
    "payment_succeeded_enabled": true,
    "payment_failed_enabled": true
  },
  "sub_merchants_enabled": false,
  "parent_merchant_id":"merchant_123",
  "metadata": {
    "city": "NY",
    "unit": "245"
  },
  "primary_business_details": [
    {
      "country": "US",
      "business": "default"
    }
  ],
  "organization_id": "org_aDQVlxlUjkkgfFO9rlwr"
}'
```

#### Merchant Account - Retrieve
```bash
curl --location 'http://localhost:8080/accounts/merchant_1744037289' \
--header 'Accept: application/json' \
--header 'api-key: dev_PY3zf8m1f0HjtqxO7QS36ZRSGMP5u9IWsmDki4fwq5iY0W8e6RoF4WMwCLvlojJM'
```

#### Merchant Account - Update
```bash
curl --location 'http://localhost:8080/accounts/merchant_1744037289' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_PY3zf8m1f0HjtqxO7QS36ZRSGMP5u9IWsmDki4fwq5iY0W8e6RoF4WMwCLvlojJM' \
--data-raw '{
  "merchant_id": "merchant_1744037289",
  "locker_id": "m0010",
  "merchant_name": "Nike New",
  "merchant_details": {
    "primary_contact_person": "John Test",
    "primary_email": "JohnTest@test.com",
    "primary_phone": "sunt laborum",
    "secondary_contact_person": "John Test2",
    "secondary_email": "JohnTest2@test.com",
    "secondary_phone": "cillum do dolor id",
    "website": "https://www.example.com",
    "about_business": "Online Retail with a wide selection of organic products for North America",
    "address": {
      "line1": "1467",
      "line2": "Harrison Street",
      "line3": "Harrison Street",
      "city": "San Fransico",
      "state": "California",
      "zip": "94122",
      "country": "US",
      "first_name":"john",
      "last_name":"Doe"
    }
  },
  "return_url": "https://google.com/success",
  "webhook_details": {
    "webhook_version": "1.0.1",
    "webhook_username": "ekart_retail",
    "webhook_password": "password_ekart@123",
    "webhook_url":"https://webhook.site",
    "payment_created_enabled": true,
    "payment_succeeded_enabled": true,
    "payment_failed_enabled": true
  },
  "sub_merchants_enabled": false,
  "parent_merchant_id":"merchant_123",
  "metadata": {
    "city": "NY",
    "unit": "245"
  },
  "primary_business_details": [
    {
      "country": "US",
      "business": "default"
    }
  ]
}'
```

#### API Key - Create
```bash
curl --location 'http://localhost:8080/api_keys/merchant_1744037289' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: test_admin' \
--data '{
  "name": "API Key 1",
  "description": null,
  "expiration": "2038-01-19T03:14:08.000Z"
}'
```

#### API Key - Retrieve
```bash
curl --location 'http://localhost:8080/api_keys/merchant_1744037289/dev_Uyyi5bbsKgcsvwgtC4EJ' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: test_admin'
```

#### API Key - Update
```bash
curl --location 'http://localhost:8080/api_keys/merchant_1744037289/dev_Uyyi5bbsKgcsvwgtC4EJ' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: test_admin' \
--data '{
  "name": "Test API Key",
  "description": "My very awesome API key",
  "expiration": null
}'
```

#### API Key - List
```bash
curl --location 'http://localhost:8080/api_keys/merchant_1744036688/list' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: test_admin'
```

#### Merchant Account - List


## 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


Testing:

The following APIs need to be validated with 
- AdminAPIKey
- the API Key associated with the configured merchant_id in the ENVs.

Tested by setting the ENV through
```bash
ROUTER__FALLBACK_MERCHANT_IDS_API_KEY_AUTH__MERCHANT_IDS="merchant_1744036688, merchant_1744037289" cargo run
```
And also tested by specifying the values in the TOML file.

#### Merchant Account - Create
```bash
curl --location 'http://localhost:8080/accounts' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_PY3zf8m1f0HjtqxO7QS36ZRSGMP5u9IWsmDki4fwq5iY0W8e6RoF4WMwCLvlojJM' \
--data-raw '{
  "merchant_id": "merchant_1744040286",
  "locker_id": "m0010",
  "merchant_name": "Nike",
  "merchant_details": {
    "primary_contact_person": "John Test",
    "primary_email": "JohnTest@test.com",
    "primary_phone": "sunt laborum",
    "secondary_contact_person": "John Test2",
    "secondary_email": "JohnTest2@test.com",
    "secondary_phone": "cillum do dolor id",
    "website": "https://www.example.com",
    "about_business": "Online Retail with a wide selection of organic products for North America",
    "address": {
      "line1": "1467",
      "line2": "Harrison Street",
      "line3": "Harrison Street",
      "city": "San Fransico",
      "state": "California",
      "zip": "94122",
      "country": "US",
      "first_name":"john",
      "last_name":"Doe"
    }
  },
  "return_url": "https://google.com/success",
  "webhook_details": {
    "webhook_version": "1.0.1",
    "webhook_username": "ekart_retail",
    "webhook_password": "password_ekart@123",
    "webhook_url":"https://webhook.site",
    "payment_created_enabled": true,
    "payment_succeeded_enabled": true,
    "payment_failed_enabled": true
  },
  "sub_merchants_enabled": false,
  "parent_merchant_id":"merchant_123",
  "metadata": {
    "city": "NY",
    "unit": "245"
  },
  "primary_business_details": [
    {
      "country": "US",
      "business": "default"
    }
  ],
  "organization_id": "org_aDQVlxlUjkkgfFO9rlwr"
}'
```

#### Merchant Account - Retrieve
```bash
curl --location 'http://localhost:8080/accounts/merchant_1744037289' \
--header 'Accept: application/json' \
--header 'api-key: dev_PY3zf8m1f0HjtqxO7QS36ZRSGMP5u9IWsmDki4fwq5iY0W8e6RoF4WMwCLvlojJM'
```

#### Merchant Account - Update
```bash
curl --location 'http://localhost:8080/accounts/merchant_1744037289' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_PY3zf8m1f0HjtqxO7QS36ZRSGMP5u9IWsmDki4fwq5iY0W8e6RoF4WMwCLvlojJM' \
--data-raw '{
  "merchant_id": "merchant_1744037289",
  "locker_id": "m0010",
  "merchant_name": "Nike New",
  "merchant_details": {
    "primary_contact_person": "John Test",
    "primary_email": "JohnTest@test.com",
    "primary_phone": "sunt laborum",
    "secondary_contact_person": "John Test2",
    "secondary_email": "JohnTest2@test.com",
    "secondary_phone": "cillum do dolor id",
    "website": "https://www.example.com",
    "about_business": "Online Retail with a wide selection of organic products for North America",
    "address": {
      "line1": "1467",
      "line2": "Harrison Street",
      "line3": "Harrison Street",
      "city": "San Fransico",
      "state": "California",
      "zip": "94122",
      "country": "US",
      "first_name":"john",
      "last_name":"Doe"
    }
  },
  "return_url": "https://google.com/success",
  "webhook_details": {
    "webhook_version": "1.0.1",
    "webhook_username": "ekart_retail",
    "webhook_password": "password_ekart@123",
    "webhook_url":"https://webhook.site",
    "payment_created_enabled": true,
    "payment_succeeded_enabled": true,
    "payment_failed_enabled": true
  },
  "sub_merchants_enabled": false,
  "parent_merchant_id":"merchant_123",
  "metadata": {
    "city": "NY",
    "unit": "245"
  },
  "primary_business_details": [
    {
      "country": "US",
      "business": "default"
    }
  ]
}'
```

#### API Key - Create
```bash
curl --location 'http://localhost:8080/api_keys/merchant_1744037289' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: test_admin' \
--data '{
  "name": "API Key 1",
  "description": null,
  "expiration": "2038-01-19T03:14:08.000Z"
}'
```

#### API Key - Retrieve
```bash
curl --location 'http://localhost:8080/api_keys/merchant_1744037289/dev_Uyyi5bbsKgcsvwgtC4EJ' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: test_admin'
```

#### API Key - Update
```bash
curl --location 'http://localhost:8080/api_keys/merchant_1744037289/dev_Uyyi5bbsKgcsvwgtC4EJ' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: test_admin' \
--data '{
  "name": "Test API Key",
  "description": "My very awesome API key",
  "expiration": null
}'
```

#### API Key - List
```bash
curl --location 'http://localhost:8080/api_keys/merchant_1744036688/list' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: test_admin'
```

#### Merchant Account - List

Patch:
diff --git a/crates/router/src/configs/secrets_transformers.rs b/crates/router/src/configs/secrets_transformers.rs
index c59b8471344..7cd4ce2d4bd 100644
--- a/crates/router/src/configs/secrets_transformers.rs
+++ b/crates/router/src/configs/secrets_transformers.rs
@@ -480,6 +480,7 @@ pub(crate) async fn fetch_raw_secrets(
         #[cfg(feature = "olap")]
         replica_database,
         secrets,
+        fallback_merchant_ids_api_key_auth: conf.fallback_merchant_ids_api_key_auth,
         locker: conf.locker,
         connectors: conf.connectors,
         forex_api,
diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs
index a4c454ec196..83591b15e36 100644
--- a/crates/router/src/configs/settings.rs
+++ b/crates/router/src/configs/settings.rs
@@ -66,6 +66,7 @@ pub struct Settings<S: SecretState> {
     pub redis: RedisSettings,
     pub log: Log,
     pub secrets: SecretStateContainer<Secrets, S>,
+    pub fallback_merchant_ids_api_key_auth: Option<FallbackMerchantIds>,
     pub locker: Locker,
     pub key_manager: SecretStateContainer<KeyManagerConfig, S>,
     pub connectors: Connectors,
@@ -676,6 +677,13 @@ pub struct Secrets {
     pub master_enc_key: Secret<String>,
 }
 
+#[derive(Debug, Default, Deserialize, Clone)]
+#[serde(default)]
+pub struct FallbackMerchantIds {
+    #[serde(deserialize_with = "deserialize_merchant_ids")]
+    pub merchant_ids: HashSet<id_type::MerchantId>,
+}
+
 #[derive(Debug, Clone, Default, Deserialize)]
 pub struct UserSettings {
     pub password_validity_in_days: u16,
@@ -1271,6 +1279,53 @@ where
     })?
 }
 
+fn deserialize_merchant_ids_inner(
+    value: impl AsRef<str>,
+) -> Result<HashSet<id_type::MerchantId>, String> {
+    let (values, errors) = value
+        .as_ref()
+        .trim()
+        .split(',')
+        .map(|s| {
+            let trimmed = s.trim();
+            id_type::MerchantId::wrap(trimmed.to_owned()).map_err(|error| {
+                format!(
+                    "Unable to deserialize `{}` as `MerchantId`: {error}",
+                    trimmed
+                )
+            })
+        })
+        .fold(
+            (HashSet::new(), Vec::new()),
+            |(mut values, mut errors), result| match result {
+                Ok(t) => {
+                    values.insert(t);
+                    (values, errors)
+                }
+                Err(error) => {
+                    errors.push(error);
+                    (values, errors)
+                }
+            },
+        );
+
+    if !errors.is_empty() {
+        Err(format!("Some errors occurred:\n{}", errors.join("\n")))
+    } else {
+        Ok(values)
+    }
+}
+
+fn deserialize_merchant_ids<'de, D>(
+    deserializer: D,
+) -> Result<HashSet<id_type::MerchantId>, D::Error>
+where
+    D: serde::Deserializer<'de>,
+{
+    let s = String::deserialize(deserializer)?;
+    deserialize_merchant_ids_inner(s).map_err(serde::de::Error::custom)
+}
+
 impl<'de> Deserialize<'de> for TenantConfig {
     fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
         #[derive(Deserialize)]
diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs
index 1c494542228..2be9058e906 100644
--- a/crates/router/src/routes/admin.rs
+++ b/crates/router/src/routes/admin.rs
@@ -186,7 +186,7 @@ pub async fn merchant_account_create(
         &req,
         json_payload.into_inner(),
         |state, _, req, _| create_merchant_account(state, req),
-        &auth::AdminApiAuth,
+        &auth::AdminApiAuthWithApiKeyFallback,
         api_locking::LockAction::NotApplicable,
     ))
     .await
@@ -249,7 +249,7 @@ pub async fn retrieve_merchant_account(
         payload,
         |state, _, req, _| get_merchant_account(state, req, None),
         auth::auth_type(
-            &auth::AdminApiAuth,
+            &auth::AdminApiAuthWithApiKeyFallbackAndMerchantIdFromRoute(merchant_id.clone()),
             &auth::JWTAuthMerchantFromRoute {
                 merchant_id,
                 // This should ideally be MerchantAccountRead, but since FE is calling this API for
@@ -350,7 +350,7 @@ pub async fn merchant_account_list(
         query_params.into_inner(),
         |state, _, request, _| list_merchant_account(state, request),
         auth::auth_type(
-            &auth::AdminApiAuth,
+            &auth::AdminApiAuthWithApiKeyFallback,
             &auth::JWTAuthMerchantFromHeader {
                 required_permission: Permission::MerchantAccountRead,
             },
@@ -409,7 +409,7 @@ pub async fn update_merchant_account(
         json_payload.into_inner(),
         |state, _, req, _| merchant_account_update(state, &merchant_id, None, req),
         auth::auth_type(
-            &auth::AdminApiAuth,
+            &auth::AdminApiAuthWithApiKeyFallbackAndMerchantIdFromRoute(merchant_id.clone()),
             &auth::JWTAuthMerchantFromRoute {
                 merchant_id: merchant_id.clone(),
                 required_permission: Permission::MerchantAccountWrite,
diff --git a/crates/router/src/routes/api_keys.rs b/crates/router/src/routes/api_keys.rs
index 911617753d4..087e295dc8e 100644
--- a/crates/router/src/routes/api_keys.rs
+++ b/crates/router/src/routes/api_keys.rs
@@ -29,7 +29,7 @@ pub async fn api_key_create(
             api_keys::create_api_key(state, payload, auth_data.key_store).await
         },
         auth::auth_type(
-            &auth::AdminApiAuthWithMerchantIdFromRoute(merchant_id.clone()),
+            &auth::AdminApiAuthWithApiKeyFallbackAndMerchantIdFromRoute(merchant_id.clone()),
             &auth::JWTAuthMerchantFromRoute {
                 merchant_id: merchant_id.clone(),
                 required_permission: Permission::MerchantApiKeyWrite,
@@ -130,7 +130,7 @@ pub async fn api_key_retrieve(
         (merchant_id.clone(), key_id.clone()),
         |state, _, (merchant_id, key_id), _| api_keys::retrieve_api_key(state, merchant_id, key_id),
         auth::auth_type(
-            &auth::AdminApiAuth,
+            &auth::AdminApiAuthWithApiKeyFallbackAndMerchantIdFromRoute(merchant_id.clone()),
             &auth::JWTAuthMerchantFromRoute {
                 merchant_id: merchant_id.clone(),
                 required_permission: Permission::MerchantApiKeyRead,
@@ -166,7 +166,7 @@ pub async fn api_key_update(
         payload,
         |state, _, payload, _| api_keys::update_api_key(state, payload),
         auth::auth_type(
-            &auth::AdminApiAuth,
+            &auth::AdminApiAuthWithApiKeyFallbackAndMerchantIdFromRoute(merchant_id.clone()),
             &auth::JWTAuthMerchantFromRoute {
                 merchant_id,
                 required_permission: Permission::MerchantApiKeyWrite,
@@ -303,7 +303,7 @@ pub async fn api_key_list(
             api_keys::list_api_keys(state, merchant_id, limit, offset).await
         },
         auth::auth_type(
-            &auth::AdminApiAuth,
+            &auth::AdminApiAuthWithApiKeyFallbackAndMerchantIdFromRoute(merchant_id.clone()),
             &auth::JWTAuthMerchantFromRoute {
                 merchant_id,
                 required_permission: Permission::MerchantApiKeyRead,
diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs
index 7e3d6802b88..3b9fcfc0d8e 100644
--- a/crates/router/src/services/authentication.rs
+++ b/crates/router/src/services/authentication.rs
@@ -654,113 +654,20 @@ where
         request_headers: &HeaderMap,
         state: &A,
     ) -> RouterResult<(AuthenticationData, AuthenticationType)> {
-        let merchant_id_from_route = self.0.clone();
-        let api_key = get_api_key(request_headers)
-            .change_context(errors::ApiErrorResponse::Unauthorized)?
-            .trim();
-        if api_key.is_empty() {
-            return Err(errors::ApiErrorResponse::Unauthorized)
-                .attach_printable("API key is empty");
-        }
-
-        let api_key = api_keys::PlaintextApiKey::from(api_key);
-        let hash_key = {
-            let config = state.conf();
-            config.api_keys.get_inner().get_hash_key()?
-        };
-        let hashed_api_key = api_key.keyed_hash(hash_key.peek());
-
-        let stored_api_key = state
-            .store()
-            .find_api_key_by_hash_optional(hashed_api_key.into())
-            .await
-            .change_context(errors::ApiErrorResponse::InternalServerError) // If retrieve failed
-            .attach_printable("Failed to retrieve API key")?
-            .ok_or(report!(errors::ApiErrorResponse::Unauthorized)) // If retrieve returned `None`
-            .attach_printable("Merchant not authenticated")?;
-
-        if stored_api_key
-            .expires_at
-            .map(|expires_at| expires_at < date_time::now())
-            .unwrap_or(false)
-        {
-            return Err(report!(errors::ApiErrorResponse::Unauthorized))
-                .attach_printable("API key has expired");
-        }
-
-        let key_manager_state = &(&state.session_state()).into();
-
-        let key_store = state
-            .store()
-            .get_merchant_key_store_by_merchant_id(
-                key_manager_state,
-                &stored_api_key.merchant_id,
-                &state.store().get_master_key().to_vec().into(),
-            )
-            .await
-            .change_context(errors::ApiErrorResponse::Unauthorized)
-            .attach_printable("Failed to fetch merchant key store for the merchant id")?;
+        let (auth_data, auth_type) = ApiKeyAuth
+            .authenticate_and_fetch(request_headers, state)
+            .await?;
 
-        let profile_id =
-            get_header_value_by_key(headers::X_PROFILE_ID.to_string(), request_headers)?
-                .map(id_type::ProfileId::from_str)
-                .transpose()
-                .change_context(errors::ValidationError::IncorrectValueProvided {
-                    field_name: "X-Profile-Id",
-                })
-                .change_context(errors::ApiErrorResponse::Unauthorized)?;
+        let merchant_id_from_route = self.0.clone();
+        let merchant_id_from_api_key = auth_data.merchant_account.get_id();
 
-        if merchant_id_from_route != stored_api_key.merchant_id {
+        if merchant_id_from_route != *merchant_id_from_api_key {
             return Err(report!(errors::ApiErrorResponse::Unauthorized)).attach_printable(
                 "Merchant ID from route and Merchant ID from api-key in header do not match",
             );
         }
 
-        let merchant = state
-            .store()
-            .find_merchant_account_by_merchant_id(
-                key_manager_state,
-                &stored_api_key.merchant_id,
-                &key_store,
-            )
-            .await
-            .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?;
-
-        // Get connected merchant account if API call is done by Platform merchant account on behalf of connected merchant account
-        let (merchant, platform_merchant_account) = if state.conf().platform.enabled {
-            get_platform_merchant_account(state, request_headers, merchant).await?
-        } else {
-            (merchant, None)
-        };
-
-        let key_store = if platform_merchant_account.is_some() {
-            state
-                .store()
-                .get_merchant_key_store_by_merchant_id(
-                    key_manager_state,
-                    merchant.get_id(),
-                    &state.store().get_master_key().to_vec().into(),
-                )
-                .await
-                .change_context(errors::ApiErrorResponse::Unauthorized)
-                .attach_printable("Failed to fetch merchant key store for the merchant id")?
-        } else {
-            key_store
-        };
-
-        let auth = AuthenticationData {
-            merchant_account: merchant,
-            platform_merchant_account,
-            key_store,
-            profile_id,
-        };
-        Ok((
-            auth.clone(),
-            AuthenticationType::ApiKey {
-                merchant_id: auth.merchant_account.get_id().clone(),
-                key_id: stored_api_key.key_id,
-            },
-        ))
+        Ok((auth_data, auth_type))
     }
 }
 
@@ -1398,6 +1305,198 @@ where
     }
 }
 
+#[derive(Debug, Default)]
+pub struct AdminApiAuthWithApiKeyFallback;
+
+#[cfg(feature = "v1")]
+#[async_trait]
+impl<A> AuthenticateAndFetch<(), A> for AdminApiAuthWithApiKeyFallback
+where
+    A: SessionStateInfo + Sync,
+{
+    async fn authenticate_and_fetch(
+        &self,
+        request_headers: &HeaderMap,
+        state: &A,
+    ) -> RouterResult<((), AuthenticationType)> {
+        let request_api_key =
+            get_api_key(request_headers).change_context(errors::ApiErrorResponse::Unauthorized)?;
+
+        let conf = state.conf();
+
+        let admin_api_key = &conf.secrets.get_inner().admin_api_key;
+
+        if request_api_key == admin_api_key.peek() {
+            return Ok(((), AuthenticationType::AdminApiKey));
+        }
+        let Some(fallback_merchant_ids) = conf.fallback_merchant_ids_api_key_auth.as_ref() else {
+            return Err(report!(errors::ApiErrorResponse::Unauthorized)).attach_printable(
+                "Api Key Authentication Failure: fallback merchant set not configured",
+            );
+        };
+
+        let api_key = api_keys::PlaintextApiKey::from(request_api_key);
+        let hash_key = conf.api_keys.get_inner().get_hash_key()?;
+        let hashed_api_key = api_key.keyed_hash(hash_key.peek());
+
+        let stored_api_key = state
+            .store()
+            .find_api_key_by_hash_optional(hashed_api_key.into())
+            .await
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("Failed to retrieve API key")?
+            .ok_or(report!(errors::ApiErrorResponse::Unauthorized))
+            .attach_printable("Merchant not authenticated")?;
+
+        if stored_api_key
+            .expires_at
+            .map(|expires_at| expires_at < date_time::now())
+            .unwrap_or(false)
+        {
+            return Err(report!(errors::ApiErrorResponse::Unauthorized))
+                .attach_printable("API key has expired");
+        }
+
+        if fallback_merchant_ids
+            .merchant_ids
+            .contains(&stored_api_key.merchant_id)
+        {
+            return Ok((
+                (),
+                AuthenticationType::ApiKey {
+                    merchant_id: stored_api_key.merchant_id,
+                    key_id: stored_api_key.key_id,
+                },
+            ));
+        }
+        Err(report!(errors::ApiErrorResponse::Unauthorized)
+            .attach_printable("Admin Authentication Failure"))
+    }
+}
+
+#[derive(Debug, Default)]
+pub struct AdminApiAuthWithApiKeyFallbackAndMerchantIdFromRoute(pub id_type::MerchantId);
+
+#[cfg(feature = "v1")]
+impl AdminApiAuthWithApiKeyFallbackAndMerchantIdFromRoute {
+    async fn fetch_merchant_key_store_and_account<A: SessionStateInfo + Sync>(
+        merchant_id: &id_type::MerchantId,
+        state: &A,
+    ) -> RouterResult<(domain::MerchantKeyStore, domain::MerchantAccount)> {
+        let key_manager_state = &(&state.session_state()).into();
+        let key_store = state
+            .store()
+            .get_merchant_key_store_by_merchant_id(
+                key_manager_state,
+                merchant_id,
+                &state.store().get_master_key().to_vec().into(),
+            )
+            .await
+            .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?;
+
+        let merchant = state
+            .store()
+            .find_merchant_account_by_merchant_id(key_manager_state, merchant_id, &key_store)
+            .await
+            .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?;
+
+        Ok((key_store, merchant))
+    }
+}
+
+#[cfg(feature = "v1")]
+#[async_trait]
+impl<A> AuthenticateAndFetch<AuthenticationData, A>
+    for AdminApiAuthWithApiKeyFallbackAndMerchantIdFromRoute
+where
+    A: SessionStateInfo + Sync,
+{
+    async fn authenticate_and_fetch(
+        &self,
+        request_headers: &HeaderMap,
+        state: &A,
+    ) -> RouterResult<(AuthenticationData, AuthenticationType)> {
+        let merchant_id_from_route: id_type::MerchantId = self.0.clone();
+        let request_api_key =
+            get_api_key(request_headers).change_context(errors::ApiErrorResponse::Unauthorized)?;
+        let conf = state.conf();
+
+        let admin_api_key: &masking::Secret<String> = &conf.secrets.get_inner().admin_api_key;
+
+        if request_api_key == admin_api_key.peek() {
+            let (key_store, merchant) =
+                Self::fetch_merchant_key_store_and_account(&merchant_id_from_route, state).await?;
+            let auth = AuthenticationData {
+                merchant_account: merchant,
+                platform_merchant_account: None,
+                key_store,
+                profile_id: None,
+            };
+            return Ok((
+                auth,
+                AuthenticationType::AdminApiAuthWithMerchantId {
+                    merchant_id: merchant_id_from_route.clone(),
+                },
+            ));
+        }
+        let Some(fallback_merchant_ids) = conf.fallback_merchant_ids_api_key_auth.as_ref() else {
+            return Err(report!(errors::ApiErrorResponse::Unauthorized)).attach_printable(
+                "Api Key Authentication Failure: fallback merchant set not configured",
+            );
+        };
+
+        let api_key = api_keys::PlaintextApiKey::from(request_api_key);
+        let hash_key = conf.api_keys.get_inner().get_hash_key()?;
+        let hashed_api_key = api_key.keyed_hash(hash_key.peek());
+
+        let stored_api_key = state
+            .store()
+            .find_api_key_by_hash_optional(hashed_api_key.into())
+            .await
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("Failed to retrieve API key")?
+            .ok_or(report!(errors::ApiErrorResponse::Unauthorized))
+            .attach_printable("Merchant not authenticated")?;
+
+        if stored_api_key
+            .expires_at
+            .map(|expires_at| expires_at < date_time::now())
+            .unwrap_or(false)
+        {
+            return Err(report!(errors::ApiErrorResponse::Unauthorized))
+                .attach_printable("API key has expired");
+        }
+
+        if fallback_merchant_ids
+            .merchant_ids
+            .contains(&stored_api_key.merchant_id)
+        {
+            let (_, api_key_merchant) =
+                Self::fetch_merchant_key_store_and_account(&stored_api_key.merchant_id, state)
+                    .await?;
+            let (route_key_store, route_merchant) =
+                Self::fetch_merchant_key_store_and_account(&merchant_id_from_route, state).await?;
+            if api_key_merchant.get_org_id() == route_merchant.get_org_id() {
+                let auth = AuthenticationData {
+                    merchant_account: route_merchant,
+                    platform_merchant_account: None,
+                    key_store: route_key_store,
+                    profile_id: None,
+                };
+                return Ok((
+                    auth.clone(),
+                    AuthenticationType::MerchantId {
+                        merchant_id: auth.merchant_account.get_id().clone(),
+                    },
+                ));
+            }
+        }
+
+        Err(report!(errors::ApiErrorResponse::Unauthorized)
+            .attach_printable("Admin Authentication Failure"))
+    }
+}
+
 /// A helper struct to extract headers from the request
 pub(crate) struct HeaderMapStruct<'a> {
     headers: &'a HeaderMap,