Bug: [BUG] update `api_key_expiry_workflow` to validate the expiry before scheduling the task



### Bug Description

We have a `process_tracker` which schedules the task to future date, and executes it whenever the time is up. We have a feature to schedule a reminder email to notify the merchants when their api_key is about to expire.
Currently, when the api_key is created with some expiry set, process tracker schedules the 1st email, 7 days prior to api_key expiry. But just in case merchant sets the expiry to next day, process tracker will schedule the email to past day which won't be executed by process tracker. 

### Expected Behavior

During the api_key expiry if the merchant sets the expiry to next day or any other day before 7 days, we need to perform a validation something like - calculate the schedule_time of 1st email during api_key creation. if it is before the current_time, don't create an entry in process_tracker.

file to include the change - 
https://github.com/juspay/hyperswitch/blob/e913bfc4958da613cd352eca9bc38b23ab7ac38e/crates/router/src/core/api_keys.rs#L193C1-L193C1

### Steps To Reproduce

1. Run below commands in 3 diff terminals
    - cargo r --features email (application)
    - SCHEDULER_FLOW=producer cargo r --bin scheduler (producer binary)
    - SCHEDULER_FLOW=consumer cargo r --bin scheduler (consumer binary)
2. Create an api_key with expiry set to next day in postman.
3. Check the process_tracker table in db which will contain the schedule_time field set to past day.


### Context For The Bug

Just to cover the edge case


### Have you spent some time to check if this bug has been raised before?

- [X] I checked and didn't find similar issue

### Have you read the Contributing Guidelines?

- [X] I have read the [Contributing Guidelines](https://github.com/juspay/hyperswitch/blob/main/docs/CONTRIBUTING.md)

### Are you willing to submit a PR?

No, but I'm happy to collaborate on a PR with someone else
diff --git a/crates/router/src/core/api_keys.rs b/crates/router/src/core/api_keys.rs
index b26a67d9fe3..129b1f96e20 100644
--- a/crates/router/src/core/api_keys.rs
+++ b/crates/router/src/core/api_keys.rs
@@ -179,6 +179,7 @@ pub async fn create_api_key(
     );
 
     // Add process to process_tracker for email reminder, only if expiry is set to future date
+    // If the `api_key` is set to expire in less than 7 days, the merchant is not notified about it's expiry
     #[cfg(feature = "email")]
     {
         if api_key.expires_at.is_some() {
@@ -200,6 +201,7 @@ pub async fn create_api_key(
 // Add api_key_expiry task to the process_tracker table.
 // Construct ProcessTrackerNew struct with all required fields, and schedule the first email.
 // After first email has been sent, update the schedule_time based on retry_count in execute_workflow().
+// A task is not scheduled if the time for the first email is in the past.
 #[cfg(feature = "email")]
 #[instrument(skip_all)]
 pub async fn add_api_key_expiry_task(
@@ -208,6 +210,21 @@ pub async fn add_api_key_expiry_task(
     expiry_reminder_days: Vec<u8>,
 ) -> Result<(), errors::ProcessTrackerError> {
     let current_time = common_utils::date_time::now();
+
+    let schedule_time = expiry_reminder_days
+        .first()
+        .and_then(|expiry_reminder_day| {
+            api_key.expires_at.map(|expires_at| {
+                expires_at.saturating_sub(time::Duration::days(i64::from(*expiry_reminder_day)))
+            })
+        });
+
+    if let Some(schedule_time) = schedule_time {
+        if schedule_time <= current_time {
+            return Ok(());
+        }
+    }
+
     let api_key_expiry_tracker = &storage::ApiKeyExpiryWorkflow {
         key_id: api_key.key_id.clone(),
         merchant_id: api_key.merchant_id.clone(),
@@ -223,14 +240,6 @@ pub async fn add_api_key_expiry_task(
             format!("unable to serialize API key expiry tracker: {api_key_expiry_tracker:?}")
         })?;
 
-    let schedule_time = expiry_reminder_days
-        .first()
-        .and_then(|expiry_reminder_day| {
-            api_key.expires_at.map(|expires_at| {
-                expires_at.saturating_sub(time::Duration::days(i64::from(*expiry_reminder_day)))
-            })
-        });
-
     let process_tracker_entry = storage::ProcessTrackerNew {
         id: generate_task_id_for_api_key_expiry_workflow(api_key.key_id.as_str()),
         name: Some(String::from(API_KEY_EXPIRY_NAME)),
@@ -356,6 +365,7 @@ pub async fn update_api_key(
 
 // Update api_key_expiry task in the process_tracker table.
 // Construct Update variant of ProcessTrackerUpdate with new tracking_data.
+// A task is not scheduled if the time for the first email is in the past.
 #[cfg(feature = "email")]
 #[instrument(skip_all)]
 pub async fn update_api_key_expiry_task(
@@ -365,10 +375,6 @@ pub async fn update_api_key_expiry_task(
 ) -> Result<(), errors::ProcessTrackerError> {
     let current_time = common_utils::date_time::now();
 
-    let task_id = generate_task_id_for_api_key_expiry_workflow(api_key.key_id.as_str());
-
-    let task_ids = vec![task_id.clone()];
-
     let schedule_time = expiry_reminder_days
         .first()
         .and_then(|expiry_reminder_day| {
@@ -377,6 +383,16 @@ pub async fn update_api_key_expiry_task(
             })
         });
 
+    if let Some(schedule_time) = schedule_time {
+        if schedule_time <= current_time {
+            return Ok(());
+        }
+    }
+
+    let task_id = generate_task_id_for_api_key_expiry_workflow(api_key.key_id.as_str());
+
+    let task_ids = vec![task_id.clone()];
+
     let updated_tracking_data = &storage::ApiKeyExpiryWorkflow {
         key_id: api_key.key_id.clone(),
         merchant_id: api_key.merchant_id.clone(),

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

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

## Description
During the api_key expiry if the merchant sets the expiry to next day or any other day before 7 days, we need to perform a validation something like - calculate the schedule_time of 1st email during api_key creation. if it is before the current_time, don't create an entry in process_tracker.

### 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
Closes #1625 


## How did you test it?
I tested the changes manually.
1. First created an API Key with expiry date after more than 7 days from today. Verified `process_tracker` entry is added.
[
![added_api](https://github.com/juspay/hyperswitch/assets/62106295/3d2e2ec9-2bfc-46c4-a0cb-6b152ee5c592)
](url)
![added_db](https://github.com/juspay/hyperswitch/assets/62106295/e055f770-f062-4503-9672-aa669a4a6e84)

2. Then created an API key with expiry date less than 7 days from today. Verified `process_tracker` entry is not added.
![not_added_api](https://github.com/juspay/hyperswitch/assets/62106295/704ebea1-43b1-4c1a-8e5b-b4e9f4bcd9d3)
![not_added_db](https://github.com/juspay/hyperswitch/assets/62106295/d3797ac8-fb04-426e-b6ea-3e5db761a5d4)


## 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 submitted code
- [ ] I added unit tests for my changes where possible
- [ ] I added a [CHANGELOG](/CHANGELOG.md) entry if applicable
