Bug: [FEATURE]  Client Secret Expiry



### Feature Description

We need to implement a TTL (Time to Live) for client secret so that once it expires there can be certain restriction on Payment Intent and even no further actions on Intent too.

### Possible Implementation

Possible implementation is to create a field in Merchant account to store the time else to take the default of 15x60secs. In order to expire the Client Secret.

### Have you spent some time to check if this feature request 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?

Yes I am willing to submit a PR!
diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs
index df245fbfad5..52524aea8c3 100644
--- a/crates/api_models/src/admin.rs
+++ b/crates/api_models/src/admin.rs
@@ -75,6 +75,10 @@ pub struct MerchantAccountCreate {
     #[cfg(not(feature = "multiple_mca"))]
     #[schema(value_type = Option<PrimaryBusinessDetails>)]
     pub primary_business_details: Option<Vec<PrimaryBusinessDetails>>,
+    ///Will be used to expire client secret after certain amount of time to be supplied in seconds
+    ///(900) for 15 mins
+    #[schema(example = 900)]
+    pub intent_fulfillment_time: Option<u32>,
 }
 
 #[derive(Clone, Debug, Deserialize, ToSchema)]
@@ -135,6 +139,10 @@ pub struct MerchantAccountUpdate {
 
     ///Default business details for connector routing
     pub primary_business_details: Option<Vec<PrimaryBusinessDetails>>,
+
+    ///Will be used to expire client secret after certain amount of time to be supplied in seconds
+    ///(900) for 15 mins
+    pub intent_fulfillment_time: Option<u32>,
 }
 
 #[derive(Clone, Debug, ToSchema, Serialize)]
@@ -201,6 +209,10 @@ pub struct MerchantAccountResponse {
     ///Default business details for connector routing
     #[schema(value_type = Vec<PrimaryBusinessDetails>)]
     pub primary_business_details: Vec<PrimaryBusinessDetails>,
+
+    ///Will be used to expire client secret after certain amount of time to be supplied in seconds
+    ///(900) for 15 mins
+    pub intent_fulfillment_time: Option<i64>,
 }
 
 #[derive(Clone, Debug, Deserialize, ToSchema, Serialize)]
diff --git a/crates/router/src/consts.rs b/crates/router/src/consts.rs
index 663f6b8dff3..e9076b881c8 100644
--- a/crates/router/src/consts.rs
+++ b/crates/router/src/consts.rs
@@ -12,6 +12,9 @@ pub(crate) const ALPHABETS: [char; 62] = [
 /// API client request timeout (in seconds)
 pub const REQUEST_TIME_OUT: u64 = 30;
 
+///Payment intent fulfillment default timeout (in seconds)
+pub const DEFAULT_FULFILLMENT_TIME: i64 = 15 * 60;
+
 // String literals
 pub(crate) const NO_ERROR_MESSAGE: &str = "No error message";
 pub(crate) const NO_ERROR_CODE: &str = "No error code";
diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs
index 6be044e5397..fbf5180b677 100644
--- a/crates/router/src/core/admin.rs
+++ b/crates/router/src/core/admin.rs
@@ -151,6 +151,7 @@ pub async fn create_merchant_account(
         locker_id: req.locker_id,
         metadata: req.metadata,
         primary_business_details,
+        intent_fulfillment_time: req.intent_fulfillment_time.map(i64::from),
     };
 
     let merchant_account = db
@@ -184,7 +185,6 @@ pub async fn get_merchant_account(
         )?,
     ))
 }
-
 pub async fn merchant_account_update(
     db: &dyn StorageInterface,
     merchant_id: &String,
@@ -258,6 +258,7 @@ pub async fn merchant_account_update(
         metadata: req.metadata,
         publishable_key: None,
         primary_business_details,
+        intent_fulfillment_time: req.intent_fulfillment_time.map(i64::from),
     };
 
     let response = db
diff --git a/crates/router/src/core/cards_info.rs b/crates/router/src/core/cards_info.rs
index c7c8cacbe16..baaf201adb4 100644
--- a/crates/router/src/core/cards_info.rs
+++ b/crates/router/src/core/cards_info.rs
@@ -28,11 +28,10 @@ pub async fn retrieve_card_info(
     let db = &*state.store;
 
     verify_iin_length(&request.card_iin)?;
-    helpers::verify_client_secret(
+    helpers::verify_payment_intent_time_and_client_secret(
         db,
-        merchant_account.storage_scheme,
+        &merchant_account,
         request.client_secret,
-        &merchant_account.merchant_id,
     )
     .await?;
 
diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs
index 8dc6b0ed7b9..3d141fc11f2 100644
--- a/crates/router/src/core/payment_methods/cards.rs
+++ b/crates/router/src/core/payment_methods/cards.rs
@@ -787,11 +787,10 @@ pub async fn list_payment_methods(
     let db = &*state.store;
     let pm_config_mapping = &state.conf.pm_filters;
 
-    let payment_intent = helpers::verify_client_secret(
+    let payment_intent = helpers::verify_payment_intent_time_and_client_secret(
         db,
-        merchant_account.storage_scheme,
+        &merchant_account,
         req.client_secret.clone(),
-        &merchant_account.merchant_id,
     )
     .await?;
 
diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs
index f2a7fdc9b83..bb81a81edaa 100644
--- a/crates/router/src/core/payments.rs
+++ b/crates/router/src/core/payments.rs
@@ -18,6 +18,7 @@ pub use self::operations::{
 };
 use self::{
     flows::{ConstructFlowSpecificData, Feature},
+    helpers::authenticate_client_secret,
     operations::{payment_complete_authorize, BoxedOperation, Operation},
 };
 use crate::{
@@ -30,9 +31,10 @@ use crate::{
     logger, pii,
     routes::AppState,
     scheduler::utils as pt_utils,
-    services,
+    services::{self, api::Authenticate},
     types::{
-        self, api,
+        self,
+        api::{self},
         storage::{self, enums as storage_enums},
     },
     utils::{Encode, OptionExt, ValueExt},
@@ -48,6 +50,7 @@ pub async fn payments_operation_core<F, Req, Op, FData>(
 ) -> RouterResult<(PaymentData<F>, Req, Option<storage::Customer>)>
 where
     F: Send + Clone + Sync,
+    Req: Authenticate,
     Op: Operation<F, Req> + Send + Sync,
 
     // To create connector flow specific interface data
@@ -82,6 +85,11 @@ where
             &merchant_account,
         )
         .await?;
+    authenticate_client_secret(
+        req.get_client_secret(),
+        &payment_data.payment_intent,
+        merchant_account.intent_fulfillment_time,
+    )?;
 
     let (operation, customer) = operation
         .to_domain()?
@@ -187,7 +195,7 @@ where
     F: Send + Clone + Sync,
     FData: Send + Sync,
     Op: Operation<F, Req> + Send + Sync + Clone,
-    Req: Debug,
+    Req: Debug + Authenticate,
     Res: transformers::ToResponse<Req, PaymentData<F>, Op>,
     // To create connector flow specific interface data
     PaymentData<F>: ConstructFlowSpecificData<F, FData, types::PaymentsResponseData>,
diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs
index 40e17d1a01d..47862e25c16 100644
--- a/crates/router/src/core/payments/helpers.rs
+++ b/crates/router/src/core/payments/helpers.rs
@@ -9,7 +9,8 @@ use common_utils::{
 use error_stack::{report, IntoReport, ResultExt};
 use masking::{ExposeOptionInterface, PeekInterface};
 use router_env::{instrument, tracing};
-use storage_models::enums;
+use storage_models::{enums, merchant_account, payment_intent};
+use time::Duration;
 use uuid::Uuid;
 
 use super::{
@@ -1185,14 +1186,29 @@ pub fn generate_mandate(
     }
 }
 
-// A function to manually authenticate the client secret
+// A function to manually authenticate the client secret with intent fulfillment time
 pub(crate) fn authenticate_client_secret(
     request_client_secret: Option<&String>,
-    payment_intent_client_secret: Option<&String>,
+    payment_intent: &payment_intent::PaymentIntent,
+    merchant_intent_fulfillment_time: Option<i64>,
 ) -> Result<(), errors::ApiErrorResponse> {
-    match (request_client_secret, payment_intent_client_secret) {
-        (Some(req_cs), Some(pi_cs)) if req_cs != pi_cs => {
-            Err(errors::ApiErrorResponse::ClientSecretInvalid)
+    match (request_client_secret, &payment_intent.client_secret) {
+        (Some(req_cs), Some(pi_cs)) => {
+            if req_cs != pi_cs {
+                Err(errors::ApiErrorResponse::ClientSecretInvalid)
+            } else {
+                //This is done to check whether the merchant_account's intent fulfillment time has expired or not
+                let payment_intent_fulfillment_deadline =
+                    payment_intent.created_at.saturating_add(Duration::seconds(
+                        merchant_intent_fulfillment_time
+                            .unwrap_or(consts::DEFAULT_FULFILLMENT_TIME),
+                    ));
+                let current_timestamp = common_utils::date_time::now();
+                fp_utils::when(
+                    current_timestamp > payment_intent_fulfillment_deadline,
+                    || Err(errors::ApiErrorResponse::ClientSecretExpired),
+                )
+            }
         }
         // If there is no client in payment intent, then it has expired
         (Some(_), None) => Err(errors::ApiErrorResponse::ClientSecretExpired),
@@ -1237,11 +1253,10 @@ pub(crate) fn validate_pm_or_token_given(
 }
 
 // A function to perform database lookup and then verify the client secret
-pub(crate) async fn verify_client_secret(
+pub(crate) async fn verify_payment_intent_time_and_client_secret(
     db: &dyn StorageInterface,
-    storage_scheme: storage_enums::MerchantStorageScheme,
+    merchant_account: &merchant_account::MerchantAccount,
     client_secret: Option<String>,
-    merchant_id: &str,
 ) -> error_stack::Result<Option<storage::PaymentIntent>, errors::ApiErrorResponse> {
     client_secret
         .async_map(|cs| async move {
@@ -1250,13 +1265,17 @@ pub(crate) async fn verify_client_secret(
             let payment_intent = db
                 .find_payment_intent_by_payment_id_merchant_id(
                     &payment_id,
-                    merchant_id,
-                    storage_scheme,
+                    &merchant_account.merchant_id,
+                    merchant_account.storage_scheme,
                 )
                 .await
                 .change_context(errors::ApiErrorResponse::PaymentNotFound)?;
 
-            authenticate_client_secret(Some(&cs), payment_intent.client_secret.as_ref())?;
+            authenticate_client_secret(
+                Some(&cs),
+                &payment_intent,
+                merchant_account.intent_fulfillment_time,
+            )?;
             Ok(payment_intent)
         })
         .await
@@ -1354,10 +1373,120 @@ mod tests {
     use super::*;
 
     #[test]
-    fn test_authenticate_client_secret() {
+    fn test_authenticate_client_secret_fulfillment_time_not_expired() {
+        let payment_intent = payment_intent::PaymentIntent {
+            id: 21,
+            payment_id: "23".to_string(),
+            merchant_id: "22".to_string(),
+            status: storage_enums::IntentStatus::RequiresCapture,
+            amount: 200,
+            currency: None,
+            amount_captured: None,
+            customer_id: None,
+            description: None,
+            return_url: None,
+            metadata: None,
+            connector_id: None,
+            shipping_address_id: None,
+            billing_address_id: None,
+            statement_descriptor_name: None,
+            statement_descriptor_suffix: None,
+            created_at: common_utils::date_time::now(),
+            modified_at: common_utils::date_time::now(),
+            last_synced: None,
+            setup_future_usage: None,
+            off_session: None,
+            client_secret: Some("1".to_string()),
+            active_attempt_id: "nopes".to_string(),
+            business_country: storage_enums::CountryAlpha2::AG,
+            business_label: "no".to_string(),
+        };
+        let req_cs = Some("1".to_string());
+        let merchant_fulfillment_time = Some(900);
+        assert!(authenticate_client_secret(
+            req_cs.as_ref(),
+            &payment_intent,
+            merchant_fulfillment_time
+        )
+        .is_ok()); // Check if the result is an Ok variant
+    }
+
+    #[test]
+    fn test_authenticate_client_secret_fulfillment_time_expired() {
+        let payment_intent = payment_intent::PaymentIntent {
+            id: 21,
+            payment_id: "23".to_string(),
+            merchant_id: "22".to_string(),
+            status: storage_enums::IntentStatus::RequiresCapture,
+            amount: 200,
+            currency: None,
+            amount_captured: None,
+            customer_id: None,
+            description: None,
+            return_url: None,
+            metadata: None,
+            connector_id: None,
+            shipping_address_id: None,
+            billing_address_id: None,
+            statement_descriptor_name: None,
+            statement_descriptor_suffix: None,
+            created_at: common_utils::date_time::now().saturating_sub(Duration::seconds(20)),
+            modified_at: common_utils::date_time::now(),
+            last_synced: None,
+            setup_future_usage: None,
+            off_session: None,
+            client_secret: Some("1".to_string()),
+            active_attempt_id: "nopes".to_string(),
+            business_country: storage_enums::CountryAlpha2::AG,
+            business_label: "no".to_string(),
+        };
+        let req_cs = Some("1".to_string());
+        let merchant_fulfillment_time = Some(10);
+        assert!(authenticate_client_secret(
+            req_cs.as_ref(),
+            &payment_intent,
+            merchant_fulfillment_time
+        )
+        .is_err())
+    }
+
+    #[test]
+    fn test_authenticate_client_secret_expired() {
+        let payment_intent = payment_intent::PaymentIntent {
+            id: 21,
+            payment_id: "23".to_string(),
+            merchant_id: "22".to_string(),
+            status: storage_enums::IntentStatus::RequiresCapture,
+            amount: 200,
+            currency: None,
+            amount_captured: None,
+            customer_id: None,
+            description: None,
+            return_url: None,
+            metadata: None,
+            connector_id: None,
+            shipping_address_id: None,
+            billing_address_id: None,
+            statement_descriptor_name: None,
+            statement_descriptor_suffix: None,
+            created_at: common_utils::date_time::now().saturating_sub(Duration::seconds(20)),
+            modified_at: common_utils::date_time::now(),
+            last_synced: None,
+            setup_future_usage: None,
+            off_session: None,
+            client_secret: None,
+            active_attempt_id: "nopes".to_string(),
+            business_country: storage_enums::CountryAlpha2::AG,
+            business_label: "no".to_string(),
+        };
         let req_cs = Some("1".to_string());
-        let pi_cs = Some("2".to_string());
-        assert!(authenticate_client_secret(req_cs.as_ref(), pi_cs.as_ref()).is_err())
+        let merchant_fulfillment_time = Some(10);
+        assert!(authenticate_client_secret(
+            req_cs.as_ref(),
+            &payment_intent,
+            merchant_fulfillment_time
+        )
+        .is_err())
     }
 }
 
diff --git a/crates/router/src/core/payments/operations/payment_complete_authorize.rs b/crates/router/src/core/payments/operations/payment_complete_authorize.rs
index 1e382179ecb..f7135c035f5 100644
--- a/crates/router/src/core/payments/operations/payment_complete_authorize.rs
+++ b/crates/router/src/core/payments/operations/payment_complete_authorize.rs
@@ -78,11 +78,6 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Co
         )
         .await?;
 
-        helpers::authenticate_client_secret(
-            request.client_secret.as_ref(),
-            payment_intent.client_secret.as_ref(),
-        )?;
-
         let browser_info = request
             .browser_info
             .clone()
diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs
index 480eef30d2d..b7990284d30 100644
--- a/crates/router/src/core/payments/operations/payment_confirm.rs
+++ b/crates/router/src/core/payments/operations/payment_confirm.rs
@@ -79,11 +79,6 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
         )
         .await?;
 
-        helpers::authenticate_client_secret(
-            request.client_secret.as_ref(),
-            payment_intent.client_secret.as_ref(),
-        )?;
-
         let browser_info = request
             .browser_info
             .clone()
diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs
index 27b819ca1f8..28fae2522b1 100644
--- a/crates/router/src/core/payments/operations/payment_session.rs
+++ b/crates/router/src/core/payments/operations/payment_session.rs
@@ -84,11 +84,6 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsSessionRequest>
 
         let amount = payment_intent.amount.into();
 
-        helpers::authenticate_client_secret(
-            Some(&request.client_secret),
-            payment_intent.client_secret.as_ref(),
-        )?;
-
         let shipping_address = helpers::get_address_for_payment_request(
             db,
             None,
diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs
index 5e22592bc00..1bccc8cbe70 100644
--- a/crates/router/src/core/payments/operations/payment_update.rs
+++ b/crates/router/src/core/payments/operations/payment_update.rs
@@ -71,11 +71,6 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
             "update",
         )?;
 
-        helpers::authenticate_client_secret(
-            request.client_secret.as_ref(),
-            payment_intent.client_secret.as_ref(),
-        )?;
-
         let (token, payment_method_type, setup_mandate) =
             helpers::get_token_pm_type_mandate_details(
                 state,
diff --git a/crates/router/src/db/merchant_account.rs b/crates/router/src/db/merchant_account.rs
index 65d190448e4..eb0bd093a8d 100644
--- a/crates/router/src/db/merchant_account.rs
+++ b/crates/router/src/db/merchant_account.rs
@@ -201,6 +201,7 @@ impl MerchantAccountInterface for MockDb {
             primary_business_details: merchant_account.primary_business_details,
             created_at: common_utils::date_time::now(),
             modified_at: common_utils::date_time::now(),
+            intent_fulfillment_time: merchant_account.intent_fulfillment_time,
         };
         accounts.push(account.clone());
         Ok(account)
diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs
index 05f3d110e9d..7839035ae99 100644
--- a/crates/router/src/routes/payment_methods.rs
+++ b/crates/router/src/routes/payment_methods.rs
@@ -75,7 +75,6 @@ pub async fn list_payment_method_api(
 ) -> HttpResponse {
     let flow = Flow::PaymentMethodsList;
     let payload = json_payload.into_inner();
-
     let (auth, _) = match auth::check_client_secret_and_get_auth(req.headers(), &payload) {
         Ok((auth, _auth_flow)) => (auth, _auth_flow),
         Err(e) => return api::log_and_return_error_response(e),
diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs
index 4a0bae0801b..4d985c095a2 100644
--- a/crates/router/src/routes/payments.rs
+++ b/crates/router/src/routes/payments.rs
@@ -321,7 +321,6 @@ pub async fn payments_confirm(
     let payment_id = path.into_inner();
     payload.payment_id = Some(payment_types::PaymentIdType::PaymentIntentId(payment_id));
     payload.confirm = Some(true);
-
     let (auth_type, auth_flow) =
         match auth::check_client_secret_and_get_auth(req.headers(), &payload) {
             Ok(auth) => auth,
diff --git a/crates/router/src/services/api.rs b/crates/router/src/services/api.rs
index 13080cf8ade..c0c30e9b883 100644
--- a/crates/router/src/services/api.rs
+++ b/crates/router/src/services/api.rs
@@ -668,7 +668,9 @@ pub trait ConnectorRedirectResponse {
 }
 
 pub trait Authenticate {
-    fn get_client_secret(&self) -> Option<&String>;
+    fn get_client_secret(&self) -> Option<&String> {
+        None
+    }
 }
 
 impl Authenticate for api_models::payments::PaymentsRequest {
@@ -683,6 +685,17 @@ impl Authenticate for api_models::payment_methods::PaymentMethodListRequest {
     }
 }
 
+impl Authenticate for api_models::payments::PaymentsSessionRequest {
+    fn get_client_secret(&self) -> Option<&String> {
+        Some(&self.client_secret)
+    }
+}
+
+impl Authenticate for api_models::payments::PaymentsRetrieveRequest {}
+impl Authenticate for api_models::payments::PaymentsCancelRequest {}
+impl Authenticate for api_models::payments::PaymentsCaptureRequest {}
+impl Authenticate for api_models::payments::PaymentsStartRequest {}
+
 pub fn build_redirection_form(form: &RedirectForm) -> maud::Markup {
     use maud::PreEscaped;
 
diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs
index 91cd26bad16..5fa4f3b53f6 100644
--- a/crates/router/src/services/authentication.rs
+++ b/crates/router/src/services/authentication.rs
@@ -20,7 +20,6 @@ use crate::{
     types::storage,
     utils::OptionExt,
 };
-
 #[async_trait]
 pub trait AuthenticateAndFetch<T, A>
 where
diff --git a/crates/router/src/types/api/admin.rs b/crates/router/src/types/api/admin.rs
index ade860b88fc..9cfd4ea2145 100644
--- a/crates/router/src/types/api/admin.rs
+++ b/crates/router/src/types/api/admin.rs
@@ -36,6 +36,7 @@ impl ForeignTryFrom<storage::MerchantAccount> for MerchantAccountResponse {
             metadata: item.metadata,
             locker_id: item.locker_id,
             primary_business_details,
+            intent_fulfillment_time: item.intent_fulfillment_time,
         })
     }
 }
diff --git a/crates/storage_models/src/merchant_account.rs b/crates/storage_models/src/merchant_account.rs
index 09af4a3d01b..f92f742d71c 100644
--- a/crates/storage_models/src/merchant_account.rs
+++ b/crates/storage_models/src/merchant_account.rs
@@ -37,6 +37,7 @@ pub struct MerchantAccount {
     pub api_key: Option<StrongSecret<String>>,
     pub created_at: time::PrimitiveDateTime,
     pub modified_at: time::PrimitiveDateTime,
+    pub intent_fulfillment_time: Option<i64>,
 }
 
 #[derive(Clone, Debug, Default, Insertable, router_derive::DebugAsDisplay)]
@@ -58,6 +59,7 @@ pub struct MerchantAccountNew {
     pub routing_algorithm: Option<serde_json::Value>,
     pub primary_business_details: serde_json::Value,
     pub api_key: Option<StrongSecret<String>>,
+    pub intent_fulfillment_time: Option<i64>,
 }
 
 #[derive(Debug)]
@@ -77,6 +79,7 @@ pub enum MerchantAccountUpdate {
         metadata: Option<pii::SecretSerdeValue>,
         routing_algorithm: Option<serde_json::Value>,
         primary_business_details: Option<serde_json::Value>,
+        intent_fulfillment_time: Option<i64>,
     },
     StorageSchemeUpdate {
         storage_scheme: storage_enums::MerchantStorageScheme,
@@ -102,6 +105,7 @@ pub struct MerchantAccountUpdateInternal {
     routing_algorithm: Option<serde_json::Value>,
     primary_business_details: Option<serde_json::Value>,
     modified_at: Option<time::PrimitiveDateTime>,
+    intent_fulfillment_time: Option<i64>,
 }
 
 impl From<MerchantAccountUpdate> for MerchantAccountUpdateInternal {
@@ -122,6 +126,7 @@ impl From<MerchantAccountUpdate> for MerchantAccountUpdateInternal {
                 locker_id,
                 metadata,
                 primary_business_details,
+                intent_fulfillment_time,
             } => Self {
                 merchant_name,
                 merchant_details,
@@ -138,6 +143,7 @@ impl From<MerchantAccountUpdate> for MerchantAccountUpdateInternal {
                 metadata,
                 primary_business_details,
                 modified_at: Some(common_utils::date_time::now()),
+                intent_fulfillment_time,
                 ..Default::default()
             },
             MerchantAccountUpdate::StorageSchemeUpdate { storage_scheme } => Self {
diff --git a/crates/storage_models/src/schema.rs b/crates/storage_models/src/schema.rs
index d6723fba9fb..650ed04342a 100644
--- a/crates/storage_models/src/schema.rs
+++ b/crates/storage_models/src/schema.rs
@@ -248,6 +248,7 @@ diesel::table! {
         api_key -> Nullable<Varchar>,
         created_at -> Timestamp,
         modified_at -> Timestamp,
+        intent_fulfillment_time -> Nullable<Int8>,
     }
 }
 
diff --git a/migrations/2023-04-19-072152_merchant_account_add_intent_fulfilment_time/down.sql b/migrations/2023-04-19-072152_merchant_account_add_intent_fulfilment_time/down.sql
new file mode 100644
index 00000000000..bfce5d026a4
--- /dev/null
+++ b/migrations/2023-04-19-072152_merchant_account_add_intent_fulfilment_time/down.sql
@@ -0,0 +1 @@
+ALTER TABLE merchant_account DROP COLUMN IF EXISTS intent_fulfillment_time; 
diff --git a/migrations/2023-04-19-072152_merchant_account_add_intent_fulfilment_time/up.sql b/migrations/2023-04-19-072152_merchant_account_add_intent_fulfilment_time/up.sql
new file mode 100644
index 00000000000..0e7bfea4359
--- /dev/null
+++ b/migrations/2023-04-19-072152_merchant_account_add_intent_fulfilment_time/up.sql
@@ -0,0 +1 @@
+ALTER TABLE merchant_account ADD COLUMN IF NOT EXISTS intent_fulfillment_time BIGINT;

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

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

## Description
We have added a new Optional field in MerchantAccount table i.e intent_fulfillment_time that will set by the merchant during the account creation and by default it is of 900secs(15 mins). So If the intent is set then the customer needs to fulfill it within that time limit or else it will expire and throw client_secret_expired

### Additional Changes

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


## How did you test it?
Written 3 tests which verifies
1. Client secret is itself mismatching or not from req and from payment intent.
2. intent_filfillment_time once expired should throw error.
3. intent_fulfillment_time not expired should pass the test.


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