Bug: feat: Get a Token with expiry for any connector payment flow



`GlobalPayments` and `Paypal` are to connectors that require tokens to make any call to their Api's.
These tokens have an expiry as well.

Need feature to get the token for any of the connector flows to proceed with the payment.
diff --git a/crates/api_models/src/enums.rs b/crates/api_models/src/enums.rs
index ec38f147dda..1bd75a634fc 100644
--- a/crates/api_models/src/enums.rs
+++ b/crates/api_models/src/enums.rs
@@ -527,6 +527,12 @@ pub enum Connector {
     Worldpay,
 }
 
+impl Connector {
+    pub fn supports_access_token(&self) -> bool {
+        matches!(self, Self::Globalpay)
+    }
+}
+
 #[derive(
     Clone,
     Copy,
diff --git a/crates/router/src/connector/aci.rs b/crates/router/src/connector/aci.rs
index 6a3cabafb68..b1ebb50a267 100644
--- a/crates/router/src/connector/aci.rs
+++ b/crates/router/src/connector/aci.rs
@@ -51,6 +51,7 @@ impl api::PaymentSync for Aci {}
 impl api::PaymentVoid for Aci {}
 impl api::PaymentCapture for Aci {}
 impl api::PaymentSession for Aci {}
+impl api::ConnectorAccessToken for Aci {}
 
 impl
     services::ConnectorIntegration<
@@ -62,6 +63,16 @@ impl
     // Not Implemented (R)
 }
 
+impl
+    services::ConnectorIntegration<
+        api::AccessTokenAuth,
+        types::AccessTokenRequestData,
+        types::AccessToken,
+    > for Aci
+{
+    // Not Implemented (R)
+}
+
 impl api::PreVerify for Aci {}
 
 impl
diff --git a/crates/router/src/connector/adyen.rs b/crates/router/src/connector/adyen.rs
index ea77bdfa36a..82fa473c735 100644
--- a/crates/router/src/connector/adyen.rs
+++ b/crates/router/src/connector/adyen.rs
@@ -52,6 +52,17 @@ impl api::PaymentSync for Adyen {}
 impl api::PaymentVoid for Adyen {}
 impl api::PaymentCapture for Adyen {}
 impl api::PreVerify for Adyen {}
+impl api::ConnectorAccessToken for Adyen {}
+
+impl
+    services::ConnectorIntegration<
+        api::AccessTokenAuth,
+        types::AccessTokenRequestData,
+        types::AccessToken,
+    > for Adyen
+{
+    // Not Implemented (R)
+}
 
 impl
     services::ConnectorIntegration<
diff --git a/crates/router/src/connector/applepay.rs b/crates/router/src/connector/applepay.rs
index c64af224f12..edcd40127cb 100644
--- a/crates/router/src/connector/applepay.rs
+++ b/crates/router/src/connector/applepay.rs
@@ -37,6 +37,17 @@ impl api::PaymentVoid for Applepay {}
 impl api::PaymentCapture for Applepay {}
 impl api::PreVerify for Applepay {}
 impl api::PaymentSession for Applepay {}
+impl api::ConnectorAccessToken for Applepay {}
+
+impl
+    services::ConnectorIntegration<
+        api::AccessTokenAuth,
+        types::AccessTokenRequestData,
+        types::AccessToken,
+    > for Applepay
+{
+    // Not Implemented (R)
+}
 
 impl
     services::ConnectorIntegration<
diff --git a/crates/router/src/connector/authorizedotnet.rs b/crates/router/src/connector/authorizedotnet.rs
index beaf03a3a61..5542af0da5a 100644
--- a/crates/router/src/connector/authorizedotnet.rs
+++ b/crates/router/src/connector/authorizedotnet.rs
@@ -42,6 +42,7 @@ impl api::PaymentSync for Authorizedotnet {}
 impl api::PaymentVoid for Authorizedotnet {}
 impl api::PaymentCapture for Authorizedotnet {}
 impl api::PaymentSession for Authorizedotnet {}
+impl api::ConnectorAccessToken for Authorizedotnet {}
 
 impl
     services::ConnectorIntegration<
@@ -53,6 +54,16 @@ impl
     // Not Implemented (R)
 }
 
+impl
+    services::ConnectorIntegration<
+        api::AccessTokenAuth,
+        types::AccessTokenRequestData,
+        types::AccessToken,
+    > for Authorizedotnet
+{
+    // Not Implemented (R)
+}
+
 impl api::PreVerify for Authorizedotnet {}
 
 impl
diff --git a/crates/router/src/connector/braintree.rs b/crates/router/src/connector/braintree.rs
index aad8c4826a1..3fd1972d5c5 100644
--- a/crates/router/src/connector/braintree.rs
+++ b/crates/router/src/connector/braintree.rs
@@ -51,6 +51,17 @@ impl api::PaymentVoid for Braintree {}
 impl api::PaymentCapture for Braintree {}
 
 impl api::PaymentSession for Braintree {}
+impl api::ConnectorAccessToken for Braintree {}
+
+impl
+    services::ConnectorIntegration<
+        api::AccessTokenAuth,
+        types::AccessTokenRequestData,
+        types::AccessToken,
+    > for Braintree
+{
+    // Not Implemented (R)
+}
 
 impl
     services::ConnectorIntegration<
@@ -279,7 +290,7 @@ impl
         data: &types::PaymentsSyncRouterData,
         res: types::Response,
     ) -> CustomResult<types::PaymentsSyncRouterData, errors::ConnectorError> {
-        logger::debug!(payment_sync_response=?res);
+        logger::debug!(payment_sync_response_braintree=?res);
         let response: braintree::BraintreePaymentsResponse = res
             .response
             .parse_struct("Braintree PaymentsResponse")
@@ -368,11 +379,11 @@ impl
         data: &types::PaymentsAuthorizeRouterData,
         res: types::Response,
     ) -> CustomResult<types::PaymentsAuthorizeRouterData, errors::ConnectorError> {
+        logger::debug!(braintreepayments_create_response=?res);
         let response: braintree::BraintreePaymentsResponse = res
             .response
             .parse_struct("Braintree PaymentsResponse")
             .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
-        logger::debug!(braintreepayments_create_response=?response);
         types::ResponseRouterData {
             response,
             data: data.clone(),
diff --git a/crates/router/src/connector/checkout.rs b/crates/router/src/connector/checkout.rs
index 191d6dfba14..89a0c308a47 100644
--- a/crates/router/src/connector/checkout.rs
+++ b/crates/router/src/connector/checkout.rs
@@ -59,6 +59,7 @@ impl api::PaymentSync for Checkout {}
 impl api::PaymentVoid for Checkout {}
 impl api::PaymentCapture for Checkout {}
 impl api::PaymentSession for Checkout {}
+impl api::ConnectorAccessToken for Checkout {}
 
 impl
     services::ConnectorIntegration<
@@ -70,6 +71,16 @@ impl
     // Not Implemented (R)
 }
 
+impl
+    services::ConnectorIntegration<
+        api::AccessTokenAuth,
+        types::AccessTokenRequestData,
+        types::AccessToken,
+    > for Checkout
+{
+    // Not Implemented (R)
+}
+
 impl api::PreVerify for Checkout {}
 
 impl
diff --git a/crates/router/src/connector/cybersource.rs b/crates/router/src/connector/cybersource.rs
index 13f292b6cdd..930ae3ab51f 100644
--- a/crates/router/src/connector/cybersource.rs
+++ b/crates/router/src/connector/cybersource.rs
@@ -165,12 +165,19 @@ impl api::PaymentSync for Cybersource {}
 impl api::PaymentVoid for Cybersource {}
 impl api::PaymentCapture for Cybersource {}
 impl api::PreVerify for Cybersource {}
+impl api::ConnectorAccessToken for Cybersource {}
 
 impl ConnectorIntegration<api::Verify, types::VerifyRequestData, types::PaymentsResponseData>
     for Cybersource
 {
 }
 
+impl ConnectorIntegration<api::AccessTokenAuth, types::AccessTokenRequestData, types::AccessToken>
+    for Cybersource
+{
+    // Not Implemented (R)
+}
+
 impl api::PaymentSession for Cybersource {}
 
 impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::PaymentsResponseData>
diff --git a/crates/router/src/connector/fiserv.rs b/crates/router/src/connector/fiserv.rs
index f1a11368ba4..0e241cefd96 100644
--- a/crates/router/src/connector/fiserv.rs
+++ b/crates/router/src/connector/fiserv.rs
@@ -63,6 +63,18 @@ impl api::ConnectorCommon for Fiserv {
     }
 }
 
+impl api::ConnectorAccessToken for Fiserv {}
+
+impl
+    services::ConnectorIntegration<
+        api::AccessTokenAuth,
+        types::AccessTokenRequestData,
+        types::AccessToken,
+    > for Fiserv
+{
+    // Not Implemented (R)
+}
+
 impl api::Payment for Fiserv {}
 
 impl api::PreVerify for Fiserv {}
diff --git a/crates/router/src/connector/globalpay.rs b/crates/router/src/connector/globalpay.rs
index 475146a466b..9e132d5695b 100644
--- a/crates/router/src/connector/globalpay.rs
+++ b/crates/router/src/connector/globalpay.rs
@@ -92,6 +92,13 @@ impl ConnectorCommon for Globalpay {
     }
 }
 
+impl api::ConnectorAccessToken for Globalpay {}
+
+impl ConnectorIntegration<api::AccessTokenAuth, types::AccessTokenRequestData, types::AccessToken>
+    for Globalpay
+{
+}
+
 impl api::Payment for Globalpay {}
 
 impl api::PreVerify for Globalpay {}
diff --git a/crates/router/src/connector/klarna.rs b/crates/router/src/connector/klarna.rs
index f3bfb0ef530..01a2aee175f 100644
--- a/crates/router/src/connector/klarna.rs
+++ b/crates/router/src/connector/klarna.rs
@@ -51,6 +51,17 @@ impl api::PaymentSync for Klarna {}
 impl api::PaymentVoid for Klarna {}
 impl api::PaymentCapture for Klarna {}
 impl api::PaymentSession for Klarna {}
+impl api::ConnectorAccessToken for Klarna {}
+
+impl
+    services::ConnectorIntegration<
+        api::AccessTokenAuth,
+        types::AccessTokenRequestData,
+        types::AccessToken,
+    > for Klarna
+{
+    // Not Implemented (R)
+}
 
 impl
     services::ConnectorIntegration<
diff --git a/crates/router/src/connector/payu.rs b/crates/router/src/connector/payu.rs
index 172675fd57e..f7ffc31b713 100644
--- a/crates/router/src/connector/payu.rs
+++ b/crates/router/src/connector/payu.rs
@@ -165,6 +165,17 @@ impl
     }
 }
 
+impl api::ConnectorAccessToken for Payu {}
+
+impl
+    services::ConnectorIntegration<
+        api::AccessTokenAuth,
+        types::AccessTokenRequestData,
+        types::AccessToken,
+    > for Payu
+{
+}
+
 impl api::PaymentSync for Payu {}
 impl
     services::ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsResponseData>
diff --git a/crates/router/src/connector/rapyd.rs b/crates/router/src/connector/rapyd.rs
index e9595b30714..d6fd8af2f44 100644
--- a/crates/router/src/connector/rapyd.rs
+++ b/crates/router/src/connector/rapyd.rs
@@ -72,6 +72,17 @@ impl ConnectorCommon for Rapyd {
     }
 }
 
+impl api::ConnectorAccessToken for Rapyd {}
+
+impl
+    services::ConnectorIntegration<
+        api::AccessTokenAuth,
+        types::AccessTokenRequestData,
+        types::AccessToken,
+    > for Rapyd
+{
+}
+
 impl api::PaymentAuthorize for Rapyd {}
 
 impl
diff --git a/crates/router/src/connector/shift4.rs b/crates/router/src/connector/shift4.rs
index ef820241ad8..e3a492f5ee1 100644
--- a/crates/router/src/connector/shift4.rs
+++ b/crates/router/src/connector/shift4.rs
@@ -95,6 +95,13 @@ impl ConnectorCommon for Shift4 {
 }
 
 impl api::Payment for Shift4 {}
+impl api::ConnectorAccessToken for Shift4 {}
+
+impl ConnectorIntegration<api::AccessTokenAuth, types::AccessTokenRequestData, types::AccessToken>
+    for Shift4
+{
+    // Not Implemented (R)
+}
 
 impl api::PreVerify for Shift4 {}
 impl ConnectorIntegration<api::Verify, types::VerifyRequestData, types::PaymentsResponseData>
diff --git a/crates/router/src/connector/stripe.rs b/crates/router/src/connector/stripe.rs
index 390645b79bd..4e49f0ec835 100644
--- a/crates/router/src/connector/stripe.rs
+++ b/crates/router/src/connector/stripe.rs
@@ -60,6 +60,17 @@ impl api::PaymentSync for Stripe {}
 impl api::PaymentVoid for Stripe {}
 impl api::PaymentCapture for Stripe {}
 impl api::PaymentSession for Stripe {}
+impl api::ConnectorAccessToken for Stripe {}
+
+impl
+    services::ConnectorIntegration<
+        api::AccessTokenAuth,
+        types::AccessTokenRequestData,
+        types::AccessToken,
+    > for Stripe
+{
+    // Not Implemented (R)
+}
 
 impl
     services::ConnectorIntegration<
diff --git a/crates/router/src/connector/worldline.rs b/crates/router/src/connector/worldline.rs
index 38e9cd36d00..bbbd1ce3e51 100644
--- a/crates/router/src/connector/worldline.rs
+++ b/crates/router/src/connector/worldline.rs
@@ -124,6 +124,13 @@ impl ConnectorCommon for Worldline {
     }
 }
 
+impl api::ConnectorAccessToken for Worldline {}
+
+impl ConnectorIntegration<api::AccessTokenAuth, types::AccessTokenRequestData, types::AccessToken>
+    for Worldline
+{
+}
+
 impl api::Payment for Worldline {}
 
 impl api::PreVerify for Worldline {}
diff --git a/crates/router/src/connector/worldpay.rs b/crates/router/src/connector/worldpay.rs
index 62dd747b169..c039bb8ed21 100644
--- a/crates/router/src/connector/worldpay.rs
+++ b/crates/router/src/connector/worldpay.rs
@@ -179,6 +179,13 @@ impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsR
     }
 }
 
+impl api::ConnectorAccessToken for Worldpay {}
+
+impl ConnectorIntegration<api::AccessTokenAuth, types::AccessTokenRequestData, types::AccessToken>
+    for Worldpay
+{
+}
+
 impl api::PaymentSync for Worldpay {}
 impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsResponseData>
     for Worldpay
diff --git a/crates/router/src/core/errors.rs b/crates/router/src/core/errors.rs
index a21289023b7..54bfb74b36d 100644
--- a/crates/router/src/core/errors.rs
+++ b/crates/router/src/core/errors.rs
@@ -64,6 +64,8 @@ pub enum StorageError {
     MockDbError,
     #[error("Customer with this id is Redacted")]
     CustomerRedacted,
+    #[error("Deserialization failure")]
+    DeserializationFailed,
 }
 
 impl From<error_stack::Report<storage_errors::DatabaseError>> for StorageError {
diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs
index c9f8515f09d..a0e616cf0c0 100644
--- a/crates/router/src/core/payments.rs
+++ b/crates/router/src/core/payments.rs
@@ -1,3 +1,4 @@
+pub mod access_token;
 pub mod flows;
 pub mod helpers;
 pub mod operations;
@@ -330,7 +331,7 @@ pub async fn call_connector_service<F, Op, Req>(
     call_connector_action: CallConnectorAction,
 ) -> RouterResult<PaymentData<F>>
 where
-    Op: Debug,
+    Op: Debug + Sync,
     F: Send + Clone,
 
     // To create connector flow specific interface data
@@ -347,21 +348,36 @@ where
 
     let stime_connector = Instant::now();
 
-    let router_data = payment_data
+    let mut router_data = payment_data
         .construct_router_data(state, connector.connector.id(), merchant_account)
         .await?;
 
-    let res = router_data
-        .decide_flows(
-            state,
-            &connector,
-            customer,
-            call_connector_action,
-            merchant_account,
-        )
-        .await;
+    let add_access_token_result = router_data
+        .add_access_token(state, &connector, merchant_account)
+        .await?;
+
+    match add_access_token_result.access_token_result {
+        Ok(access_token) => router_data.access_token = access_token,
+        Err(connector_error) => router_data.response = Err(connector_error),
+    }
+
+    let router_data_res = if !(add_access_token_result.connector_supports_access_token
+        && router_data.access_token.is_none())
+    {
+        router_data
+            .decide_flows(
+                state,
+                &connector,
+                customer,
+                call_connector_action,
+                merchant_account,
+            )
+            .await
+    } else {
+        Ok(router_data)
+    };
 
-    let response = res
+    let response = router_data_res
         .async_and_then(|response| async {
             let operation = helpers::response_operation::<F, Req>();
             let payment_data = operation
diff --git a/crates/router/src/core/payments/access_token.rs b/crates/router/src/core/payments/access_token.rs
new file mode 100644
index 00000000000..d2af78c7839
--- /dev/null
+++ b/crates/router/src/core/payments/access_token.rs
@@ -0,0 +1,157 @@
+use std::fmt::Debug;
+
+use common_utils::ext_traits::AsyncExt;
+use error_stack::{IntoReport, ResultExt};
+
+use crate::{
+    core::{
+        errors::{self, RouterResult},
+        payments,
+    },
+    routes::AppState,
+    services,
+    types::{self, api as api_types, storage},
+};
+
+/// This function replaces the request and response type of routerdata with the
+/// request and response type passed
+/// # Arguments
+///
+/// * `router_data` - original router data
+/// * `request` - new request
+/// * `response` - new response
+pub fn router_data_type_conversion<F1, F2, Req1, Req2, Res1, Res2>(
+    router_data: types::RouterData<F1, Req1, Res1>,
+    request: Req2,
+    response: Result<Res2, types::ErrorResponse>,
+) -> types::RouterData<F2, Req2, Res2> {
+    types::RouterData {
+        flow: std::marker::PhantomData,
+        request,
+        response,
+        merchant_id: router_data.merchant_id,
+        address: router_data.address,
+        amount_captured: router_data.amount_captured,
+        auth_type: router_data.auth_type,
+        connector: router_data.connector,
+        connector_auth_type: router_data.connector_auth_type,
+        connector_meta_data: router_data.connector_meta_data,
+        description: router_data.description,
+        router_return_url: router_data.router_return_url,
+        payment_id: router_data.payment_id,
+        payment_method: router_data.payment_method,
+        payment_method_id: router_data.payment_method_id,
+        return_url: router_data.return_url,
+        status: router_data.status,
+        attempt_id: router_data.attempt_id,
+        access_token: router_data.access_token,
+    }
+}
+
+pub async fn add_access_token<
+    F: Clone + 'static,
+    Req: Debug + Clone + 'static,
+    Res: Debug + Clone + 'static,
+>(
+    state: &AppState,
+    connector: &api_types::ConnectorData,
+    merchant_account: &storage::MerchantAccount,
+    router_data: &types::RouterData<F, Req, Res>,
+) -> RouterResult<types::AddAccessTokenResult> {
+    if connector.connector_name.supports_access_token() {
+        let merchant_id = &merchant_account.merchant_id;
+        let store = &*state.store;
+        let old_access_token = store
+            .get_access_token(merchant_id, connector.connector.id())
+            .await
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("DB error when accessing the access token")?;
+
+        let res = match old_access_token {
+            Some(access_token) => Ok(Some(access_token)),
+            None => {
+                let cloned_router_data = router_data.clone();
+                let refresh_token_request_data = types::AccessTokenRequestData::try_from(
+                    router_data.connector_auth_type.clone(),
+                )
+                .into_report()
+                .attach_printable(
+                    "Could not create access token request, invalid connector account credentials",
+                )?;
+
+                let refresh_token_response_data: Result<types::AccessToken, types::ErrorResponse> =
+                    Err(types::ErrorResponse::default());
+                let refresh_token_router_data =
+                    router_data_type_conversion::<_, api_types::AccessTokenAuth, _, _, _, _>(
+                        cloned_router_data,
+                        refresh_token_request_data,
+                        refresh_token_response_data,
+                    );
+                refresh_connector_auth(
+                    state,
+                    connector,
+                    merchant_account,
+                    &refresh_token_router_data,
+                )
+                .await?
+                .async_map(|access_token| async {
+                    //Store the access token in db
+                    let store = &*state.store;
+                    // This error should not be propagated, we don't want payments to fail once we have
+                    // the access token, the next request will create new access token
+                    let _ = store
+                        .set_access_token(
+                            merchant_id,
+                            connector.connector.id(),
+                            access_token.clone(),
+                        )
+                        .await
+                        .change_context(errors::ApiErrorResponse::InternalServerError)
+                        .attach_printable("DB error when setting the access token");
+                    Some(access_token)
+                })
+                .await
+            }
+        };
+
+        Ok(types::AddAccessTokenResult {
+            access_token_result: res,
+            connector_supports_access_token: true,
+        })
+    } else {
+        Ok(types::AddAccessTokenResult {
+            access_token_result: Err(types::ErrorResponse::default()),
+            connector_supports_access_token: false,
+        })
+    }
+}
+
+pub async fn refresh_connector_auth(
+    state: &AppState,
+    connector: &api_types::ConnectorData,
+    _merchant_account: &storage::MerchantAccount,
+    router_data: &types::RouterData<
+        api_types::AccessTokenAuth,
+        types::AccessTokenRequestData,
+        types::AccessToken,
+    >,
+) -> RouterResult<Result<types::AccessToken, types::ErrorResponse>> {
+    let connector_integration: services::BoxedConnectorIntegration<
+        '_,
+        api_types::AccessTokenAuth,
+        types::AccessTokenRequestData,
+        types::AccessToken,
+    > = connector.connector.get_connector_integration();
+
+    let access_token_router_data = services::execute_connector_processing_step(
+        state,
+        connector_integration,
+        router_data,
+        payments::CallConnectorAction::Trigger,
+    )
+    .await
+    .change_context(errors::ApiErrorResponse::InternalServerError)
+    .attach_printable("Could not refresh access token")?;
+
+    Ok(access_token_router_data.response)
+}
diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs
index d685daa34a4..f46be7e07c9 100644
--- a/crates/router/src/core/payments/flows.rs
+++ b/crates/router/src/core/payments/flows.rs
@@ -38,4 +38,15 @@ pub trait Feature<F, T> {
         Self: Sized,
         F: Clone,
         dyn api::Connector: services::ConnectorIntegration<F, T, types::PaymentsResponseData>;
+
+    async fn add_access_token<'a>(
+        &self,
+        state: &AppState,
+        connector: &api::ConnectorData,
+        merchant_account: &storage::MerchantAccount,
+    ) -> RouterResult<types::AddAccessTokenResult>
+    where
+        F: Clone,
+        Self: Sized,
+        dyn api::Connector: services::ConnectorIntegration<F, T, types::PaymentsResponseData>;
 }
diff --git a/crates/router/src/core/payments/flows/authorize_flow.rs b/crates/router/src/core/payments/flows/authorize_flow.rs
index dc7fe558aa1..909feb27d51 100644
--- a/crates/router/src/core/payments/flows/authorize_flow.rs
+++ b/crates/router/src/core/payments/flows/authorize_flow.rs
@@ -5,7 +5,7 @@ use crate::{
     core::{
         errors::{ConnectorErrorExt, RouterResult},
         mandate,
-        payments::{self, transformers, PaymentData},
+        payments::{self, access_token, transformers, PaymentData},
     },
     routes::AppState,
     scheduler::metrics,
@@ -68,6 +68,15 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
 
         resp
     }
+
+    async fn add_access_token<'a>(
+        &self,
+        state: &AppState,
+        connector: &api::ConnectorData,
+        merchant_account: &storage::MerchantAccount,
+    ) -> RouterResult<types::AddAccessTokenResult> {
+        access_token::add_access_token(state, connector, merchant_account, self).await
+    }
 }
 
 impl types::PaymentsAuthorizeRouterData {
diff --git a/crates/router/src/core/payments/flows/cancel_flow.rs b/crates/router/src/core/payments/flows/cancel_flow.rs
index 2dfb74505c5..40ba12c072b 100644
--- a/crates/router/src/core/payments/flows/cancel_flow.rs
+++ b/crates/router/src/core/payments/flows/cancel_flow.rs
@@ -4,7 +4,7 @@ use super::{ConstructFlowSpecificData, Feature};
 use crate::{
     core::{
         errors::{ConnectorErrorExt, RouterResult},
-        payments::{self, transformers, PaymentData},
+        payments::{self, access_token, transformers, PaymentData},
     },
     routes::AppState,
     services,
@@ -52,6 +52,15 @@ impl Feature<api::Void, types::PaymentsCancelData>
         )
         .await
     }
+
+    async fn add_access_token<'a>(
+        &self,
+        state: &AppState,
+        connector: &api::ConnectorData,
+        merchant_account: &storage::MerchantAccount,
+    ) -> RouterResult<types::AddAccessTokenResult> {
+        access_token::add_access_token(state, connector, merchant_account, self).await
+    }
 }
 
 impl types::PaymentsCancelRouterData {
diff --git a/crates/router/src/core/payments/flows/capture_flow.rs b/crates/router/src/core/payments/flows/capture_flow.rs
index 7b84d8f5029..e192dc7fc68 100644
--- a/crates/router/src/core/payments/flows/capture_flow.rs
+++ b/crates/router/src/core/payments/flows/capture_flow.rs
@@ -4,7 +4,7 @@ use super::ConstructFlowSpecificData;
 use crate::{
     core::{
         errors::{ConnectorErrorExt, RouterResult},
-        payments::{self, transformers, Feature, PaymentData},
+        payments::{self, access_token, transformers, Feature, PaymentData},
     },
     routes::AppState,
     services,
@@ -53,6 +53,15 @@ impl Feature<api::Capture, types::PaymentsCaptureData>
         )
         .await
     }
+
+    async fn add_access_token<'a>(
+        &self,
+        state: &AppState,
+        connector: &api::ConnectorData,
+        merchant_account: &storage::MerchantAccount,
+    ) -> RouterResult<types::AddAccessTokenResult> {
+        access_token::add_access_token(state, connector, merchant_account, self).await
+    }
 }
 
 impl types::PaymentsCaptureRouterData {
diff --git a/crates/router/src/core/payments/flows/psync_flow.rs b/crates/router/src/core/payments/flows/psync_flow.rs
index fafc0d6d03a..61127956c0a 100644
--- a/crates/router/src/core/payments/flows/psync_flow.rs
+++ b/crates/router/src/core/payments/flows/psync_flow.rs
@@ -4,7 +4,7 @@ use super::{ConstructFlowSpecificData, Feature};
 use crate::{
     core::{
         errors::{ConnectorErrorExt, RouterResult},
-        payments::{self, transformers, PaymentData},
+        payments::{self, access_token, transformers, PaymentData},
     },
     routes::AppState,
     services,
@@ -54,6 +54,15 @@ impl Feature<api::PSync, types::PaymentsSyncData>
         )
         .await
     }
+
+    async fn add_access_token<'a>(
+        &self,
+        state: &AppState,
+        connector: &api::ConnectorData,
+        merchant_account: &storage::MerchantAccount,
+    ) -> RouterResult<types::AddAccessTokenResult> {
+        access_token::add_access_token(state, connector, merchant_account, self).await
+    }
 }
 
 impl types::PaymentsSyncRouterData {
diff --git a/crates/router/src/core/payments/flows/session_flow.rs b/crates/router/src/core/payments/flows/session_flow.rs
index c105e62e850..c6e09329482 100644
--- a/crates/router/src/core/payments/flows/session_flow.rs
+++ b/crates/router/src/core/payments/flows/session_flow.rs
@@ -6,7 +6,7 @@ use super::{ConstructFlowSpecificData, Feature};
 use crate::{
     core::{
         errors::{self, ConnectorErrorExt, RouterResult},
-        payments::{self, transformers, PaymentData},
+        payments::{self, access_token, transformers, PaymentData},
     },
     routes, services,
     types::{self, api, storage},
@@ -53,6 +53,15 @@ impl Feature<api::Session, types::PaymentsSessionData> for types::PaymentsSessio
         )
         .await
     }
+
+    async fn add_access_token<'a>(
+        &self,
+        state: &routes::AppState,
+        connector: &api::ConnectorData,
+        merchant_account: &storage::MerchantAccount,
+    ) -> RouterResult<types::AddAccessTokenResult> {
+        access_token::add_access_token(state, connector, merchant_account, self).await
+    }
 }
 
 fn create_gpay_session_token(
diff --git a/crates/router/src/core/payments/flows/verfiy_flow.rs b/crates/router/src/core/payments/flows/verfiy_flow.rs
index 284f2cd4647..51265e67be5 100644
--- a/crates/router/src/core/payments/flows/verfiy_flow.rs
+++ b/crates/router/src/core/payments/flows/verfiy_flow.rs
@@ -5,7 +5,7 @@ use crate::{
     core::{
         errors::{ConnectorErrorExt, RouterResult},
         mandate,
-        payments::{self, transformers, PaymentData},
+        payments::{self, access_token, transformers, PaymentData},
     },
     routes::AppState,
     services,
@@ -52,6 +52,15 @@ impl Feature<api::Verify, types::VerifyRequestData> for types::VerifyRouterData
         )
         .await
     }
+
+    async fn add_access_token<'a>(
+        &self,
+        state: &AppState,
+        connector: &api::ConnectorData,
+        merchant_account: &storage::MerchantAccount,
+    ) -> RouterResult<types::AddAccessTokenResult> {
+        access_token::add_access_token(state, connector, merchant_account, self).await
+    }
 }
 
 impl types::VerifyRouterData {
diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs
index e7e04472084..0471f337c9a 100644
--- a/crates/router/src/core/payments/transformers.rs
+++ b/crates/router/src/core/payments/transformers.rs
@@ -98,6 +98,7 @@ where
         request: T::try_from(payment_data.clone())?,
         response: response.map_or_else(|| Err(types::ErrorResponse::default()), Ok),
         amount_captured: payment_data.payment_intent.amount_captured,
+        access_token: None,
     };
 
     Ok(router_data)
diff --git a/crates/router/src/core/refunds.rs b/crates/router/src/core/refunds.rs
index b1face64dde..c0762d53577 100644
--- a/crates/router/src/core/refunds.rs
+++ b/crates/router/src/core/refunds.rs
@@ -7,7 +7,8 @@ use crate::{
     consts,
     core::{
         errors::{self, ConnectorErrorExt, RouterResponse, RouterResult, StorageErrorExt},
-        payments, utils as core_utils,
+        payments::{self, access_token},
+        utils as core_utils,
     },
     db, logger,
     routes::AppState,
@@ -114,7 +115,7 @@ pub async fn trigger_refund_to_gateway(
 
     validator::validate_for_valid_refunds(payment_attempt)?;
 
-    let router_data = core_utils::construct_refund_router_data(
+    let mut router_data = core_utils::construct_refund_router_data(
         state,
         &connector_id,
         merchant_account,
@@ -125,23 +126,38 @@ pub async fn trigger_refund_to_gateway(
     )
     .await?;
 
-    logger::debug!(?router_data);
-    let connector_integration: services::BoxedConnectorIntegration<
-        '_,
-        api::Execute,
-        types::RefundsData,
-        types::RefundsResponseData,
-    > = connector.connector.get_connector_integration();
-    let router_data = services::execute_connector_processing_step(
-        state,
-        connector_integration,
-        &router_data,
-        payments::CallConnectorAction::Trigger,
-    )
-    .await
-    .map_err(|error| error.to_refund_failed_response())?;
+    let add_access_token_result =
+        access_token::add_access_token(state, &connector, merchant_account, &router_data).await?;
+
+    logger::debug!(refund_router_data=?router_data);
+
+    match add_access_token_result.access_token_result {
+        Ok(access_token) => router_data.access_token = access_token,
+        Err(connector_error) => router_data.response = Err(connector_error),
+    }
+
+    let router_data_res = if !(add_access_token_result.connector_supports_access_token
+        && router_data.access_token.is_none())
+    {
+        let connector_integration: services::BoxedConnectorIntegration<
+            '_,
+            api::Execute,
+            types::RefundsData,
+            types::RefundsResponseData,
+        > = connector.connector.get_connector_integration();
+        services::execute_connector_processing_step(
+            state,
+            connector_integration,
+            &router_data,
+            payments::CallConnectorAction::Trigger,
+        )
+        .await
+        .map_err(|error| error.to_refund_failed_response())?
+    } else {
+        router_data
+    };
 
-    let refund_update = match router_data.response {
+    let refund_update = match router_data_res.response {
         Err(err) => storage::RefundUpdate::ErrorUpdate {
             refund_status: Some(enums::RefundStatus::Failure),
             refund_error_message: Some(err.message),
@@ -263,7 +279,7 @@ pub async fn sync_refund_with_gateway(
 
     let currency = payment_attempt.currency.get_required_value("currency")?;
 
-    let router_data = core_utils::construct_refund_router_data::<api::RSync>(
+    let mut router_data = core_utils::construct_refund_router_data::<api::RSync>(
         state,
         &connector_id,
         merchant_account,
@@ -274,22 +290,38 @@ pub async fn sync_refund_with_gateway(
     )
     .await?;
 
-    let connector_integration: services::BoxedConnectorIntegration<
-        '_,
-        api::RSync,
-        types::RefundsData,
-        types::RefundsResponseData,
-    > = connector.connector.get_connector_integration();
-    let router_data = services::execute_connector_processing_step(
-        state,
-        connector_integration,
-        &router_data,
-        payments::CallConnectorAction::Trigger,
-    )
-    .await
-    .map_err(|error| error.to_refund_failed_response())?;
+    let add_access_token_result =
+        access_token::add_access_token(state, &connector, merchant_account, &router_data).await?;
+
+    logger::debug!(refund_retrieve_router_data=?router_data);
+
+    match add_access_token_result.access_token_result {
+        Ok(access_token) => router_data.access_token = access_token,
+        Err(connector_error) => router_data.response = Err(connector_error),
+    }
+
+    let router_data_res = if !(add_access_token_result.connector_supports_access_token
+        && router_data.access_token.is_none())
+    {
+        let connector_integration: services::BoxedConnectorIntegration<
+            '_,
+            api::RSync,
+            types::RefundsData,
+            types::RefundsResponseData,
+        > = connector.connector.get_connector_integration();
+        services::execute_connector_processing_step(
+            state,
+            connector_integration,
+            &router_data,
+            payments::CallConnectorAction::Trigger,
+        )
+        .await
+        .map_err(|error| error.to_refund_failed_response())?
+    } else {
+        router_data
+    };
 
-    let refund_update = match router_data.response {
+    let refund_update = match router_data_res.response {
         Err(error_message) => storage::RefundUpdate::ErrorUpdate {
             refund_status: None,
             refund_error_message: Some(error_message.message),
diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs
index ce050694b80..fd50cf807d9 100644
--- a/crates/router/src/core/utils.rs
+++ b/crates/router/src/core/utils.rs
@@ -80,6 +80,7 @@ pub async fn construct_refund_router_data<'a, F>(
             connector_refund_id: refund.connector_refund_id.clone().unwrap_or_default(),
             refund_status: refund.refund_status,
         }),
+        access_token: None,
     };
 
     Ok(router_data)
diff --git a/crates/router/src/db.rs b/crates/router/src/db.rs
index 7e3594d00b4..285a0d238c8 100644
--- a/crates/router/src/db.rs
+++ b/crates/router/src/db.rs
@@ -42,6 +42,7 @@ pub trait StorageInterface:
     + events::EventInterface
     + merchant_account::MerchantAccountInterface
     + merchant_connector_account::MerchantConnectorAccountInterface
+    + merchant_connector_account::ConnectorAccessToken
     + locker_mock_up::LockerMockUpInterface
     + payment_intent::PaymentIntentInterface
     + payment_method::PaymentMethodInterface
diff --git a/crates/router/src/db/merchant_connector_account.rs b/crates/router/src/db/merchant_connector_account.rs
index 347506c15d6..ac3bda274a9 100644
--- a/crates/router/src/db/merchant_connector_account.rs
+++ b/crates/router/src/db/merchant_connector_account.rs
@@ -1,13 +1,99 @@
-use error_stack::IntoReport;
+use common_utils::ext_traits::{ByteSliceExt, Encode};
+use error_stack::{IntoReport, ResultExt};
 use masking::ExposeInterface;
 
 use super::{MockDb, Store};
 use crate::{
     connection::pg_connection,
     core::errors::{self, CustomResult},
-    types::storage,
+    services::logger,
+    types::{self, storage},
 };
 
+#[async_trait::async_trait]
+pub trait ConnectorAccessToken {
+    async fn get_access_token(
+        &self,
+        merchant_id: &str,
+        connector_name: &str,
+    ) -> CustomResult<Option<types::AccessToken>, errors::StorageError>;
+
+    async fn set_access_token(
+        &self,
+        merchant_id: &str,
+        connector_name: &str,
+        access_token: types::AccessToken,
+    ) -> CustomResult<(), errors::StorageError>;
+}
+
+#[async_trait::async_trait]
+impl ConnectorAccessToken for Store {
+    async fn get_access_token(
+        &self,
+        merchant_id: &str,
+        connector_name: &str,
+    ) -> CustomResult<Option<types::AccessToken>, errors::StorageError> {
+        //TODO: Handle race condition
+        // This function should acquire a global lock on some resource, if access token is already
+        // being refreshed by other request then wait till it finishes and use the same access token
+        let key = format!("access_token_{merchant_id}_{connector_name}");
+        let maybe_token = self
+            .redis_conn
+            .get_key::<Option<Vec<u8>>>(&key)
+            .await
+            .change_context(errors::StorageError::KVError)
+            .attach_printable("DB error when getting access token")?;
+
+        let access_token: Option<types::AccessToken> = maybe_token
+            .map(|token| token.parse_struct("AccessToken"))
+            .transpose()
+            .change_context(errors::ParsingError)
+            .change_context(errors::StorageError::DeserializationFailed)?;
+
+        Ok(access_token)
+    }
+
+    async fn set_access_token(
+        &self,
+        merchant_id: &str,
+        connector_name: &str,
+        access_token: types::AccessToken,
+    ) -> CustomResult<(), errors::StorageError> {
+        let key = format!("access_token_{merchant_id}_{connector_name}");
+        let serialized_access_token =
+            Encode::<types::AccessToken>::encode_to_string_of_json(&access_token)
+                .change_context(errors::StorageError::SerializationFailed)?;
+        self.redis_conn
+            .set_key_with_expiry(&key, serialized_access_token, access_token.expires)
+            .await
+            .map_err(|error| {
+                logger::error!(access_token_kv_error=?error);
+                errors::StorageError::KVError
+            })
+            .into_report()
+    }
+}
+
+#[async_trait::async_trait]
+impl ConnectorAccessToken for MockDb {
+    async fn get_access_token(
+        &self,
+        _merchant_id: &str,
+        _connector_name: &str,
+    ) -> CustomResult<Option<types::AccessToken>, errors::StorageError> {
+        Ok(None)
+    }
+
+    async fn set_access_token(
+        &self,
+        _merchant_id: &str,
+        _connector_name: &str,
+        _access_token: types::AccessToken,
+    ) -> CustomResult<(), errors::StorageError> {
+        Ok(())
+    }
+}
+
 #[async_trait::async_trait]
 pub trait MerchantConnectorAccountInterface {
     async fn find_merchant_connector_account_by_merchant_id_connector(
diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs
index 5daccb82aa4..52c4e756f39 100644
--- a/crates/router/src/types.rs
+++ b/crates/router/src/types.rs
@@ -32,6 +32,9 @@ pub type RefundsRouterData<F> = RouterData<F, RefundsData, RefundsResponseData>;
 pub type RefundExecuteRouterData = RouterData<api::Execute, RefundsData, RefundsResponseData>;
 pub type RefundSyncRouterData = RouterData<api::RSync, RefundsData, RefundsResponseData>;
 
+pub type RefreshTokenRouterData =
+    RouterData<api::AccessTokenAuth, AccessTokenRequestData, AccessToken>;
+
 pub type PaymentsResponseRouterData<R> =
     ResponseRouterData<api::Authorize, R, PaymentsAuthorizeData, PaymentsResponseData>;
 pub type PaymentsCancelResponseRouterData<R> =
@@ -80,6 +83,7 @@ pub struct RouterData<Flow, Request, Response> {
     pub auth_type: storage_enums::AuthenticationType,
     pub connector_meta_data: Option<serde_json::Value>,
     pub amount_captured: Option<i64>,
+    pub access_token: Option<AccessToken>,
 
     /// Contains flow-specific data required to construct a request and send it to the connector.
     pub request: Request,
@@ -151,10 +155,21 @@ pub struct VerifyRequestData {
 }
 
 #[derive(Debug, Clone)]
-pub struct PaymentsTransactionResponse {
-    pub resource_id: ResponseId,
-    pub redirection_data: Option<services::RedirectForm>,
-    pub redirect: bool,
+pub struct AccessTokenRequestData {
+    pub app_id: String,
+    pub id: Option<String>,
+    // Add more keys if required
+}
+
+pub struct AddAccessTokenResult {
+    pub access_token_result: Result<Option<AccessToken>, ErrorResponse>,
+    pub connector_supports_access_token: bool,
+}
+
+#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
+pub struct AccessToken {
+    pub token: String,
+    pub expires: i64,
 }
 
 #[derive(Debug, Clone)]
@@ -307,6 +322,29 @@ impl ErrorResponse {
     }
 }
 
+impl TryFrom<ConnectorAuthType> for AccessTokenRequestData {
+    type Error = errors::ApiErrorResponse;
+    fn try_from(connector_auth: ConnectorAuthType) -> Result<Self, Self::Error> {
+        match connector_auth {
+            ConnectorAuthType::HeaderKey { api_key } => Ok(Self {
+                app_id: api_key,
+                id: None,
+            }),
+            ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self {
+                app_id: api_key,
+                id: Some(key1),
+            }),
+            ConnectorAuthType::SignatureKey { api_key, key1, .. } => Ok(Self {
+                app_id: api_key,
+                id: Some(key1),
+            }),
+            _ => Err(errors::ApiErrorResponse::InvalidDataValue {
+                field_name: "connector_account_details",
+            }),
+        }
+    }
+}
+
 impl From<errors::ApiErrorResponse> for ErrorResponse {
     fn from(error: errors::ApiErrorResponse) -> Self {
         Self {
diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs
index b5354041f88..c9833904e8c 100644
--- a/crates/router/src/types/api.rs
+++ b/crates/router/src/types/api.rs
@@ -21,6 +21,14 @@ use crate::{
     types::{self, api::enums as api_enums},
 };
 
+#[derive(Clone, Debug)]
+pub struct AccessTokenAuth;
+
+pub trait ConnectorAccessToken:
+    ConnectorIntegration<AccessTokenAuth, types::AccessTokenRequestData, types::AccessToken>
+{
+}
+
 pub trait ConnectorCommon {
     /// Name of the connector (in lowercase).
     fn id(&self) -> &'static str;
@@ -76,7 +84,7 @@ pub trait ConnectorCommonExt<Flow, Req, Resp>:
 pub trait Router {}
 
 pub trait Connector:
-    Send + Refund + Payment + Debug + ConnectorRedirectResponse + IncomingWebhook
+    Send + Refund + Payment + Debug + ConnectorRedirectResponse + IncomingWebhook + ConnectorAccessToken
 {
 }
 
@@ -84,8 +92,15 @@ pub struct Re;
 
 pub struct Pe;
 
-impl<T: Refund + Payment + Debug + ConnectorRedirectResponse + Send + IncomingWebhook> Connector
-    for T
+impl<
+        T: Refund
+            + Payment
+            + Debug
+            + ConnectorRedirectResponse
+            + Send
+            + IncomingWebhook
+            + ConnectorAccessToken,
+    > Connector for T
 {
 }
 
diff --git a/crates/router/tests/connectors/aci.rs b/crates/router/tests/connectors/aci.rs
index 749dfef4a91..6425be8e1a7 100644
--- a/crates/router/tests/connectors/aci.rs
+++ b/crates/router/tests/connectors/aci.rs
@@ -56,6 +56,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData {
         address: PaymentAddress::default(),
         connector_meta_data: None,
         amount_captured: None,
+        access_token: None,
     }
 }
 
@@ -92,6 +93,7 @@ fn construct_refund_router_data<F>() -> types::RefundsRouterData<F> {
         address: PaymentAddress::default(),
         connector_meta_data: None,
         amount_captured: None,
+        access_token: None,
     }
 }
 
diff --git a/crates/router/tests/connectors/authorizedotnet.rs b/crates/router/tests/connectors/authorizedotnet.rs
index ca9102ec47a..679c8b95653 100644
--- a/crates/router/tests/connectors/authorizedotnet.rs
+++ b/crates/router/tests/connectors/authorizedotnet.rs
@@ -56,6 +56,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData {
         address: PaymentAddress::default(),
         connector_meta_data: None,
         amount_captured: None,
+        access_token: None,
     }
 }
 
@@ -91,6 +92,7 @@ fn construct_refund_router_data<F>() -> types::RefundsRouterData<F> {
         payment_method_id: None,
         address: PaymentAddress::default(),
         amount_captured: None,
+        access_token: None,
     }
 }
 
diff --git a/crates/router/tests/connectors/checkout.rs b/crates/router/tests/connectors/checkout.rs
index ad83a494163..0ac4da7e61e 100644
--- a/crates/router/tests/connectors/checkout.rs
+++ b/crates/router/tests/connectors/checkout.rs
@@ -53,6 +53,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData {
         address: PaymentAddress::default(),
         connector_meta_data: None,
         amount_captured: None,
+        access_token: None,
     }
 }
 
@@ -88,6 +89,7 @@ fn construct_refund_router_data<F>() -> types::RefundsRouterData<F> {
         payment_method_id: None,
         address: PaymentAddress::default(),
         amount_captured: None,
+        access_token: None,
     }
 }
 
diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs
index 1e2864627d1..0fd79e47c20 100644
--- a/crates/router/tests/connectors/utils.rs
+++ b/crates/router/tests/connectors/utils.rs
@@ -178,6 +178,7 @@ pub trait ConnectorActions: Connector {
             address: info.map_or(PaymentAddress::default(), |a| a.address.unwrap()),
             connector_meta_data: self.get_connector_meta(),
             amount_captured: None,
+            access_token: None,
         }
     }
 }

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

- [x] New feature

## Description
<!-- Describe your changes in detail -->
This PR will add the support for creating the access token for connectors which need it. 


## 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 its an obvious bug or documentation fix
that will have little conversation).
-->
Once the routerdata is constructed, there is a next step which will check if the connector needs access token and then either get the access token ( which is currently stored in redis, with expiry ) or refresh it by calling the connector. A separate flow has been introduced to support this feature called `UpdateAuth`.


## 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)?
-->
Manual, Compiler guided.


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