Bug: [FEATURE]: Api for enable/disable Blocklist guard



This Api mentioned will be for toggling the `blocklist-guard` for a particular merchant. So the changes will be as mentioned:

> It will try to find a config associated with that merchant (guard_blocklist_for_{merchant_id}) which in tern is a boolean config.
> If present it will enable/disable it accordingly.
> If not present it will create a config and assign it appropriate value.

## So what happens if the guard is enabled for a particular merchant:
He will have the ability to block certain payment instruments on basis of
>fingerprint 
>card-bins
>extended-bins. 

Moreover, if guard is enabled, fingerprint generation for each and every payment attempt is insured and every payment will be tested against the blocklist of the merchant.

## If guard is disabled:
None of the above steps will be valid and every payment attempt will have fingerprint as null.

## Testing
The curl for the above mentioned api is as follows:
`POST /blocklist/toggle?status=true`

```
curl --location --request POST 'http://127.0.0.1:8080/blocklist/toggle?status=false' \
--header 'api-key: dev_xiikraRGPeYjPzsef71DjOllw0hy4M9feqqSBCdzJc9XT2kPay3MJnY4o6Ui93jS'
```

Enabled response:
```
{
    "blocklist_guard_status": "blocklist guard is enabled"
}
```

Disabled response:
```
{
    "blocklist_guard_status": "blocklist guard is disabled"
}
```
diff --git a/crates/api_models/src/blocklist.rs b/crates/api_models/src/blocklist.rs
index 888b9106ccc..9672b6de6ee 100644
--- a/crates/api_models/src/blocklist.rs
+++ b/crates/api_models/src/blocklist.rs
@@ -22,6 +22,11 @@ pub struct BlocklistResponse {
     pub created_at: time::PrimitiveDateTime,
 }
 
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
+pub struct ToggleBlocklistResponse {
+    pub blocklist_guard_status: String,
+}
+
 pub type AddToBlocklistResponse = BlocklistResponse;
 pub type DeleteFromBlocklistResponse = BlocklistResponse;
 
@@ -39,6 +44,14 @@ fn default_list_limit() -> u16 {
     10
 }
 
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
+pub struct ToggleBlocklistQuery {
+    #[schema(value_type = BlocklistDataKind)]
+    pub status: bool,
+}
+
 impl ApiEventMetric for BlocklistRequest {}
 impl ApiEventMetric for BlocklistResponse {}
+impl ApiEventMetric for ToggleBlocklistResponse {}
 impl ApiEventMetric for ListBlocklistQuery {}
+impl ApiEventMetric for ToggleBlocklistQuery {}
diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs
index 982072e8c09..1936300ee14 100644
--- a/crates/openapi/src/openapi.rs
+++ b/crates/openapi/src/openapi.rs
@@ -151,6 +151,7 @@ Never share your secret api keys. Keep them guarded and secure.
         routes::blocklist::remove_entry_from_blocklist,
         routes::blocklist::list_blocked_payment_methods,
         routes::blocklist::add_entry_to_blocklist,
+        routes::blocklist::toggle_blocklist_guard,
 
         // Routes for payouts
         routes::payouts::payouts_create,
@@ -450,6 +451,7 @@ Never share your secret api keys. Keep them guarded and secure.
         api_models::payments::PaymentLinkStatus,
         api_models::blocklist::BlocklistRequest,
         api_models::blocklist::BlocklistResponse,
+        api_models::blocklist::ToggleBlocklistResponse,
         api_models::blocklist::ListBlocklistQuery,
         api_models::enums::BlocklistDataKind
     )),
diff --git a/crates/openapi/src/routes/blocklist.rs b/crates/openapi/src/routes/blocklist.rs
index bf683259e08..741925b93d2 100644
--- a/crates/openapi/src/routes/blocklist.rs
+++ b/crates/openapi/src/routes/blocklist.rs
@@ -1,3 +1,19 @@
+#[utoipa::path(
+    post,
+    path = "/blocklist/toggle",
+    params (
+        ("status" = bool, Query, description = "Boolean value to enable/disable blocklist"),
+    ),
+    responses(
+        (status = 200, description = "Blocklist guard enabled/disabled", body = ToggleBlocklistResponse),
+        (status = 400, description = "Invalid Data")
+    ),
+    tag = "Blocklist",
+    operation_id = "Toggle blocklist guard for a particular merchant",
+    security(("api_key" = []))
+)]
+pub async fn toggle_blocklist_guard() {}
+
 #[utoipa::path(
     post,
     path = "/blocklist",
diff --git a/crates/router/src/core/blocklist.rs b/crates/router/src/core/blocklist.rs
index 85845602449..12a0802517c 100644
--- a/crates/router/src/core/blocklist.rs
+++ b/crates/router/src/core/blocklist.rs
@@ -39,3 +39,13 @@ pub async fn list_blocklist_entries(
         .await
         .map(services::ApplicationResponse::Json)
 }
+
+pub async fn toggle_blocklist_guard(
+    state: AppState,
+    merchant_account: domain::MerchantAccount,
+    query: api_blocklist::ToggleBlocklistQuery,
+) -> RouterResponse<api_blocklist::ToggleBlocklistResponse> {
+    utils::toggle_blocklist_guard_for_merchant(&state, merchant_account.merchant_id, query)
+        .await
+        .map(services::ApplicationResponse::Json)
+}
diff --git a/crates/router/src/core/blocklist/utils.rs b/crates/router/src/core/blocklist/utils.rs
index bc0a7335f1e..43701c80786 100644
--- a/crates/router/src/core/blocklist/utils.rs
+++ b/crates/router/src/core/blocklist/utils.rs
@@ -4,6 +4,7 @@ use common_utils::{
     crypto::{self, SignMessage},
     errors::CustomResult,
 };
+use diesel_models::configs;
 use error_stack::{IntoReport, ResultExt};
 #[cfg(feature = "aws_kms")]
 use external_services::aws_kms;
@@ -85,6 +86,56 @@ pub async fn delete_entry_from_blocklist(
     Ok(blocklist_entry.foreign_into())
 }
 
+pub async fn toggle_blocklist_guard_for_merchant(
+    state: &AppState,
+    merchant_id: String,
+    query: api_blocklist::ToggleBlocklistQuery,
+) -> CustomResult<api_blocklist::ToggleBlocklistResponse, errors::ApiErrorResponse> {
+    let key = get_blocklist_guard_key(merchant_id.as_str());
+    let maybe_guard = state.store.find_config_by_key(&key).await;
+    let new_config = configs::ConfigNew {
+        key: key.clone(),
+        config: query.status.to_string(),
+    };
+    match maybe_guard {
+        Ok(_config) => {
+            let updated_config = configs::ConfigUpdate::Update {
+                config: Some(query.status.to_string()),
+            };
+            state
+                .store
+                .update_config_by_key(&key, updated_config)
+                .await
+                .change_context(errors::ApiErrorResponse::InternalServerError)
+                .attach_printable("Error enabling the blocklist guard")?;
+        }
+        Err(e) if e.current_context().is_db_not_found() => {
+            state
+                .store
+                .insert_config(new_config)
+                .await
+                .change_context(errors::ApiErrorResponse::InternalServerError)
+                .attach_printable("Error enabling the blocklist guard")?;
+        }
+        Err(e) => {
+            logger::error!(error=?e);
+            Err(e)
+                .change_context(errors::ApiErrorResponse::InternalServerError)
+                .attach_printable("Error enabling the blocklist guard")?;
+        }
+    };
+    let guard_status = if query.status { "enabled" } else { "disabled" };
+    Ok(api_blocklist::ToggleBlocklistResponse {
+        blocklist_guard_status: guard_status.to_string(),
+    })
+}
+
+/// Provides the identifier for the specific merchant's blocklist guard config
+#[inline(always)]
+pub fn get_blocklist_guard_key(merchant_id: &str) -> String {
+    format!("guard_blocklist_for_{merchant_id}")
+}
+
 pub async fn list_blocklist_entries_for_merchant(
     state: &AppState,
     merchant_id: String,
diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs
index f71592c5448..651d3c0026f 100644
--- a/crates/router/src/routes/app.rs
+++ b/crates/router/src/routes/app.rs
@@ -658,6 +658,9 @@ impl Blocklist {
                     .route(web::post().to(blocklist::add_entry_to_blocklist))
                     .route(web::delete().to(blocklist::remove_entry_from_blocklist)),
             )
+            .service(
+                web::resource("/toggle").route(web::post().to(blocklist::toggle_blocklist_guard)),
+            )
     }
 }
 
diff --git a/crates/router/src/routes/blocklist.rs b/crates/router/src/routes/blocklist.rs
index 9c93f49ab83..87072d2c777 100644
--- a/crates/router/src/routes/blocklist.rs
+++ b/crates/router/src/routes/blocklist.rs
@@ -117,3 +117,41 @@ pub async fn list_blocked_payment_methods(
     ))
     .await
 }
+
+#[utoipa::path(
+    post,
+    path = "/blocklist/toggle",
+    params (
+        ("status" = bool, Query, description = "Boolean value to enable/disable blocklist"),
+    ),
+    responses(
+        (status = 200, description = "Blocklist guard enabled/disabled", body = ToggleBlocklistResponse),
+        (status = 400, description = "Invalid Data")
+    ),
+    tag = "Blocklist",
+    operation_id = "Toggle blocklist guard for a particular merchant",
+    security(("api_key" = []))
+)]
+pub async fn toggle_blocklist_guard(
+    state: web::Data<AppState>,
+    req: HttpRequest,
+    query_payload: web::Query<api_blocklist::ToggleBlocklistQuery>,
+) -> HttpResponse {
+    let flow = Flow::ListBlocklist;
+    Box::pin(api::server_wrap(
+        flow,
+        state,
+        &req,
+        query_payload.into_inner(),
+        |state, auth: auth::AuthenticationData, query| {
+            blocklist::toggle_blocklist_guard(state, auth.merchant_account, query)
+        },
+        auth::auth_type(
+            &auth::ApiKeyAuth,
+            &auth::JWTAuth(Permission::MerchantAccountWrite),
+            req.headers(),
+        ),
+        api_locking::LockAction::NotApplicable,
+    ))
+    .await
+}
diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs
index 042e89fdd52..1636ed3a764 100644
--- a/crates/router/src/routes/lock_utils.rs
+++ b/crates/router/src/routes/lock_utils.rs
@@ -63,6 +63,7 @@ impl From<Flow> for ApiIdentifier {
             Flow::AddToBlocklist => Self::Blocklist,
             Flow::DeleteFromBlocklist => Self::Blocklist,
             Flow::ListBlocklist => Self::Blocklist,
+            Flow::ToggleBlocklistGuard => Self::Blocklist,
 
             Flow::MerchantConnectorsCreate
             | Flow::MerchantConnectorsRetrieve
diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs
index 11e6f9c0ed8..4344acf89ff 100644
--- a/crates/router_env/src/logger/types.rs
+++ b/crates/router_env/src/logger/types.rs
@@ -201,6 +201,8 @@ pub enum Flow {
     DeleteFromBlocklist,
     /// List entries from blocklist
     ListBlocklist,
+    /// Toggle blocklist for merchant
+    ToggleBlocklistGuard,
     /// Incoming Webhook Receive
     IncomingWebhookReceive,
     /// Validate payment method flow
diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json
index 7f234661b51..b6531771e11 100644
--- a/openapi/openapi_spec.json
+++ b/openapi/openapi_spec.json
@@ -1233,6 +1233,45 @@
         ]
       }
     },
+    "/blocklist/toggle": {
+      "post": {
+        "tags": [
+          "Blocklist"
+        ],
+        "operationId": "Toggle blocklist guard for a particular merchant",
+        "parameters": [
+          {
+            "name": "status",
+            "in": "query",
+            "description": "Boolean value to enable/disable blocklist",
+            "required": true,
+            "schema": {
+              "type": "boolean"
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "Blocklist guard enabled/disabled",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/ToggleBlocklistResponse"
+                }
+              }
+            }
+          },
+          "400": {
+            "description": "Invalid Data"
+          }
+        },
+        "security": [
+          {
+            "api_key": []
+          }
+        ]
+      }
+    },
     "/customers": {
       "post": {
         "tags": [
@@ -16114,6 +16153,17 @@
           }
         }
       },
+      "ToggleBlocklistResponse": {
+        "type": "object",
+        "required": [
+          "blocklist_guard_status"
+        ],
+        "properties": {
+          "blocklist_guard_status": {
+            "type": "string"
+          }
+        }
+      },
       "TouchNGoRedirection": {
         "type": "object"
       },

## 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 Api mentioned will be for toggling the `blocklist-guard` for a particular merchant. So the changes will be as mentioned:

> It will try to find a config associated with that merchant (guard_blocklist_for_{merchant_id}) which in tern is a boolean config.
> If present it will enable/disable it accordingly.
> If not present it will create a config and assign it appropriate value.

More in the linked issue.

### Additional Changes

- [x] 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)?
-->
This can be found out in the linked issue.

## 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
- [ ] I added a [CHANGELOG](/CHANGELOG.md) entry if applicable
