Bug: [FEATURE] Store necessary Card details in Payment methods table



### Feature Description

Will add support to add Card details in payment method table.
Currently in Payment method customers API, locker call is made for every card payment method to fetch details. Will fetch it from Payment method table instead to reduce latency.

### Possible Implementation

Currently in Payment method customers API, locker call is made for every card payment method to fetch details. Will fetch it from Payment method table instead to reduce latency.

### Have you spent some time checking if this feature request has been raised before?

- [X] I checked and didn't find a 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/payment_methods.rs b/crates/api_models/src/payment_methods.rs
index f795513c6ef..4e67ed4d45f 100644
--- a/crates/api_models/src/payment_methods.rs
+++ b/crates/api_models/src/payment_methods.rs
@@ -144,6 +144,20 @@ pub struct PaymentMethodResponse {
     pub created: Option<time::PrimitiveDateTime>,
 }
 
+#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
+pub enum PaymentMethodsData {
+    Card(CardDetailsPaymentMethod),
+}
+#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
+pub struct CardDetailsPaymentMethod {
+    pub last4_digits: Option<String>,
+    pub issuer_country: Option<String>,
+    pub expiry_month: Option<masking::Secret<String>>,
+    pub expiry_year: Option<masking::Secret<String>>,
+    pub nick_name: Option<masking::Secret<String>>,
+    pub card_holder_name: Option<masking::Secret<String>>,
+}
+
 #[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
 pub struct CardDetailFromLocker {
     pub scheme: Option<String>,
@@ -172,6 +186,36 @@ pub struct CardDetailFromLocker {
     pub nick_name: Option<masking::Secret<String>>,
 }
 
+impl From<CardDetailsPaymentMethod> for CardDetailFromLocker {
+    fn from(item: CardDetailsPaymentMethod) -> Self {
+        Self {
+            scheme: None,
+            issuer_country: item.issuer_country,
+            last4_digits: item.last4_digits,
+            card_number: None,
+            expiry_month: item.expiry_month,
+            expiry_year: item.expiry_year,
+            card_token: None,
+            card_holder_name: item.card_holder_name,
+            card_fingerprint: None,
+            nick_name: item.nick_name,
+        }
+    }
+}
+
+impl From<CardDetailFromLocker> for CardDetailsPaymentMethod {
+    fn from(item: CardDetailFromLocker) -> Self {
+        Self {
+            issuer_country: item.issuer_country,
+            last4_digits: item.last4_digits,
+            expiry_month: item.expiry_month,
+            expiry_year: item.expiry_year,
+            nick_name: item.nick_name,
+            card_holder_name: item.card_holder_name,
+        }
+    }
+}
+
 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema, PartialEq, Eq)]
 pub struct PaymentExperienceTypes {
     /// The payment experience enabled
diff --git a/crates/diesel_models/src/encryption.rs b/crates/diesel_models/src/encryption.rs
index 604a69e4d36..acb8b5f13e1 100644
--- a/crates/diesel_models/src/encryption.rs
+++ b/crates/diesel_models/src/encryption.rs
@@ -7,7 +7,7 @@ use diesel::{
 };
 use masking::Secret;
 
-#[derive(Debug, AsExpression, Clone, serde::Serialize, serde::Deserialize)]
+#[derive(Debug, AsExpression, Clone, serde::Serialize, serde::Deserialize, Eq, PartialEq)]
 #[diesel(sql_type = diesel::sql_types::Binary)]
 #[repr(transparent)]
 pub struct Encryption {
diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs
index be69d3fa5a3..8029c20038a 100644
--- a/crates/diesel_models/src/payment_method.rs
+++ b/crates/diesel_models/src/payment_method.rs
@@ -4,7 +4,7 @@ use masking::Secret;
 use serde::{Deserialize, Serialize};
 use time::PrimitiveDateTime;
 
-use crate::{enums as storage_enums, schema::payment_methods};
+use crate::{encryption::Encryption, enums as storage_enums, schema::payment_methods};
 
 #[derive(Clone, Debug, Eq, PartialEq, Identifiable, Queryable)]
 #[diesel(table_name = payment_methods)]
@@ -32,6 +32,7 @@ pub struct PaymentMethod {
     pub payment_method_issuer: Option<String>,
     pub payment_method_issuer_code: Option<storage_enums::PaymentMethodIssuerCode>,
     pub metadata: Option<pii::SecretSerdeValue>,
+    pub payment_method_data: Option<Encryption>,
 }
 
 #[derive(Clone, Debug, Eq, PartialEq, Insertable, Queryable, router_derive::DebugAsDisplay)]
@@ -57,6 +58,7 @@ pub struct PaymentMethodNew {
     pub created_at: PrimitiveDateTime,
     pub last_modified: PrimitiveDateTime,
     pub metadata: Option<pii::SecretSerdeValue>,
+    pub payment_method_data: Option<Encryption>,
 }
 
 impl Default for PaymentMethodNew {
@@ -84,6 +86,7 @@ impl Default for PaymentMethodNew {
             created_at: now,
             last_modified: now,
             metadata: Option::default(),
+            payment_method_data: Option::default(),
         }
     }
 }
diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs
index 71e49ea37b4..66bd11dccb2 100644
--- a/crates/diesel_models/src/schema.rs
+++ b/crates/diesel_models/src/schema.rs
@@ -638,6 +638,7 @@ diesel::table! {
         payment_method_issuer -> Nullable<Varchar>,
         payment_method_issuer_code -> Nullable<PaymentMethodIssuerCode>,
         metadata -> Nullable<Json>,
+        payment_method_data -> Nullable<Bytea>,
     }
 }
 
diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs
index 7155a5b3acb..7396edc0cbd 100644
--- a/crates/router/src/core/payment_methods/cards.rs
+++ b/crates/router/src/core/payment_methods/cards.rs
@@ -7,9 +7,9 @@ use api_models::{
     admin::{self, PaymentMethodsEnabled},
     enums::{self as api_enums},
     payment_methods::{
-        CardNetworkTypes, PaymentExperienceTypes, RequestPaymentMethodTypes, RequiredFieldInfo,
-        ResponsePaymentMethodIntermediate, ResponsePaymentMethodTypes,
-        ResponsePaymentMethodsEnabled,
+        CardDetailsPaymentMethod, CardNetworkTypes, PaymentExperienceTypes, PaymentMethodsData,
+        RequestPaymentMethodTypes, RequiredFieldInfo, ResponsePaymentMethodIntermediate,
+        ResponsePaymentMethodTypes, ResponsePaymentMethodsEnabled,
     },
     payments::BankCodeResponse,
 };
@@ -43,7 +43,10 @@ use crate::{
     services,
     types::{
         api::{self, PaymentMethodCreateExt},
-        domain::{self, types::decrypt},
+        domain::{
+            self,
+            types::{decrypt, encrypt_optional, AsyncLift},
+        },
         storage::{self, enums},
         transformers::{ForeignFrom, ForeignInto},
     },
@@ -58,6 +61,7 @@ pub async fn create_payment_method(
     payment_method_id: &str,
     merchant_id: &str,
     pm_metadata: Option<serde_json::Value>,
+    payment_method_data: Option<Encryption>,
 ) -> errors::CustomResult<storage::PaymentMethod, errors::StorageError> {
     let response = db
         .insert_payment_method(storage::PaymentMethodNew {
@@ -69,6 +73,7 @@ pub async fn create_payment_method(
             payment_method_issuer: req.payment_method_issuer.clone(),
             scheme: req.card_network.clone(),
             metadata: pm_metadata.map(masking::Secret::new),
+            payment_method_data,
             ..storage::PaymentMethodNew::default()
         })
         .await?;
@@ -81,6 +86,7 @@ pub async fn add_payment_method(
     state: &routes::AppState,
     req: api::PaymentMethodCreate,
     merchant_account: &domain::MerchantAccount,
+    key_store: &domain::MerchantKeyStore,
 ) -> errors::RouterResponse<api::PaymentMethodResponse> {
     req.validate()?;
     let merchant_id = &merchant_account.merchant_id;
@@ -118,6 +124,15 @@ pub async fn add_payment_method(
     let (resp, is_duplicate) = response?;
     if !is_duplicate {
         let pm_metadata = resp.metadata.as_ref().map(|data| data.peek());
+
+        let pm_card_details = resp
+            .card
+            .as_ref()
+            .map(|card| PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone())));
+
+        let pm_data_encrypted =
+            create_encrypted_payment_method_data(key_store, pm_card_details).await;
+
         create_payment_method(
             &*state.store,
             &req,
@@ -125,6 +140,7 @@ pub async fn add_payment_method(
             &resp.payment_method_id,
             &resp.merchant_id,
             pm_metadata.cloned(),
+            pm_data_encrypted,
         )
         .await
         .change_context(errors::ApiErrorResponse::InternalServerError)
@@ -140,6 +156,7 @@ pub async fn update_customer_payment_method(
     merchant_account: domain::MerchantAccount,
     req: api::PaymentMethodUpdate,
     payment_method_id: &str,
+    key_store: domain::MerchantKeyStore,
 ) -> errors::RouterResponse<api::PaymentMethodResponse> {
     let db = &*state.store;
     let pm = db
@@ -171,7 +188,7 @@ pub async fn update_customer_payment_method(
             .as_ref()
             .map(|card_network| card_network.to_string()),
     };
-    add_payment_method(state, new_pm, &merchant_account).await
+    add_payment_method(state, new_pm, &merchant_account, &key_store).await
 }
 
 // Wrapper function to switch lockers
@@ -1751,6 +1768,8 @@ pub async fn list_customer_payment_method(
     .await
     .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?;
 
+    let key = key_store.key.get_inner().peek();
+
     let is_requires_cvv = db
         .find_config_by_key(format!("{}_requires_cvv", merchant_account.merchant_id).as_str())
         .await;
@@ -1782,11 +1801,13 @@ pub async fn list_customer_payment_method(
     for pm in resp.into_iter() {
         let parent_payment_method_token = generate_id(consts::ID_LENGTH, "token");
         let hyperswitch_token = generate_id(consts::ID_LENGTH, "token");
+
         let card = if pm.payment_method == enums::PaymentMethod::Card {
-            Some(get_lookup_key_from_locker(state, &hyperswitch_token, &pm).await?)
+            get_card_details(&pm, key, state, &hyperswitch_token).await?
         } else {
             None
         };
+
         #[cfg(feature = "payouts")]
         let pmd = if pm.payment_method == enums::PaymentMethod::BankTransfer {
             Some(
@@ -1872,6 +1893,34 @@ pub async fn list_customer_payment_method(
     Ok(services::ApplicationResponse::Json(response))
 }
 
+async fn get_card_details(
+    pm: &payment_method::PaymentMethod,
+    key: &[u8],
+    state: &routes::AppState,
+    hyperswitch_token: &str,
+) -> errors::RouterResult<Option<api::CardDetailFromLocker>> {
+    let mut card_decrypted =
+        decrypt::<serde_json::Value, masking::WithType>(pm.payment_method_data.clone(), key)
+            .await
+            .change_context(errors::StorageError::DecryptionError)
+            .attach_printable("unable to decrypt card details")
+            .ok()
+            .flatten()
+            .map(|x| x.into_inner().expose())
+            .and_then(|v| serde_json::from_value::<PaymentMethodsData>(v).ok())
+            .map(|pmd| match pmd {
+                PaymentMethodsData::Card(crd) => api::CardDetailFromLocker::from(crd),
+            });
+
+    card_decrypted = if let Some(mut crd) = card_decrypted {
+        crd.scheme = pm.scheme.clone();
+        Some(crd)
+    } else {
+        Some(get_lookup_key_from_locker(state, hyperswitch_token, pm).await?)
+    };
+    Ok(card_decrypted)
+}
+
 pub async fn get_lookup_key_from_locker(
     state: &routes::AppState,
     payment_token: &str,
@@ -2191,3 +2240,33 @@ pub async fn delete_payment_method(
         },
     ))
 }
+
+pub async fn create_encrypted_payment_method_data(
+    key_store: &domain::MerchantKeyStore,
+    pm_data: Option<PaymentMethodsData>,
+) -> Option<Encryption> {
+    let key = key_store.key.get_inner().peek();
+
+    let pm_data_encrypted: Option<Encryption> = pm_data
+        .as_ref()
+        .map(utils::Encode::<PaymentMethodsData>::encode_to_value)
+        .transpose()
+        .change_context(errors::StorageError::SerializationFailed)
+        .attach_printable("Unable to convert payment method data to a value")
+        .unwrap_or_else(|err| {
+            logger::error!(err=?err);
+            None
+        })
+        .map(masking::Secret::<_, masking::WithType>::new)
+        .async_lift(|inner| encrypt_optional(inner, key))
+        .await
+        .change_context(errors::StorageError::EncryptionError)
+        .attach_printable("Unable to encrypt payment method data")
+        .unwrap_or_else(|err| {
+            logger::error!(err=?err);
+            None
+        })
+        .map(|details| details.into());
+
+    pm_data_encrypted
+}
diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs
index bb8eb772d1b..3c86ef3284b 100644
--- a/crates/router/src/core/payments.rs
+++ b/crates/router/src/core/payments.rs
@@ -668,6 +668,7 @@ where
                 call_connector_action,
                 merchant_account,
                 connector_request,
+                key_store,
             )
             .await
     } else {
@@ -721,6 +722,7 @@ where
             CallConnectorAction::Trigger,
             merchant_account,
             None,
+            key_store,
         );
 
         join_handlers.push(res);
diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs
index 041ce93b899..0de210fddd6 100644
--- a/crates/router/src/core/payments/flows.rs
+++ b/crates/router/src/core/payments/flows.rs
@@ -33,6 +33,7 @@ pub trait ConstructFlowSpecificData<F, Req, Res> {
     ) -> RouterResult<types::RouterData<F, Req, Res>>;
 }
 
+#[allow(clippy::too_many_arguments)]
 #[async_trait]
 pub trait Feature<F, T> {
     async fn decide_flows<'a>(
@@ -43,6 +44,7 @@ pub trait Feature<F, T> {
         call_connector_action: payments::CallConnectorAction,
         merchant_account: &domain::MerchantAccount,
         connector_request: Option<services::Request>,
+        key_store: &domain::MerchantKeyStore,
     ) -> RouterResult<Self>
     where
         Self: Sized,
diff --git a/crates/router/src/core/payments/flows/approve_flow.rs b/crates/router/src/core/payments/flows/approve_flow.rs
index 5a17dba2c94..faa15d27316 100644
--- a/crates/router/src/core/payments/flows/approve_flow.rs
+++ b/crates/router/src/core/payments/flows/approve_flow.rs
@@ -48,6 +48,7 @@ impl Feature<api::Approve, types::PaymentsApproveData>
         _call_connector_action: payments::CallConnectorAction,
         _merchant_account: &domain::MerchantAccount,
         _connector_request: Option<services::Request>,
+        _key_store: &domain::MerchantKeyStore,
     ) -> RouterResult<Self> {
         Err(ApiErrorResponse::NotImplemented {
             message: NotImplementedMessage::Reason("Flow not supported".to_string()),
diff --git a/crates/router/src/core/payments/flows/authorize_flow.rs b/crates/router/src/core/payments/flows/authorize_flow.rs
index 0cfc7db03b5..dd7f050bbf6 100644
--- a/crates/router/src/core/payments/flows/authorize_flow.rs
+++ b/crates/router/src/core/payments/flows/authorize_flow.rs
@@ -57,6 +57,7 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
         call_connector_action: payments::CallConnectorAction,
         merchant_account: &domain::MerchantAccount,
         connector_request: Option<services::Request>,
+        key_store: &domain::MerchantKeyStore,
     ) -> RouterResult<Self> {
         let connector_integration: services::BoxedConnectorIntegration<
             '_,
@@ -87,6 +88,7 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
                 maybe_customer,
                 merchant_account,
                 self.request.payment_method_type,
+                key_store,
             )
             .await;
 
diff --git a/crates/router/src/core/payments/flows/cancel_flow.rs b/crates/router/src/core/payments/flows/cancel_flow.rs
index a8cf41188de..911791f499a 100644
--- a/crates/router/src/core/payments/flows/cancel_flow.rs
+++ b/crates/router/src/core/payments/flows/cancel_flow.rs
@@ -47,6 +47,7 @@ impl Feature<api::Void, types::PaymentsCancelData>
         call_connector_action: payments::CallConnectorAction,
         _merchant_account: &domain::MerchantAccount,
         connector_request: Option<services::Request>,
+        _key_store: &domain::MerchantKeyStore,
     ) -> RouterResult<Self> {
         metrics::PAYMENT_CANCEL_COUNT.add(
             &metrics::CONTEXT,
diff --git a/crates/router/src/core/payments/flows/capture_flow.rs b/crates/router/src/core/payments/flows/capture_flow.rs
index 0f522e6ae1a..41d98267d0e 100644
--- a/crates/router/src/core/payments/flows/capture_flow.rs
+++ b/crates/router/src/core/payments/flows/capture_flow.rs
@@ -48,6 +48,7 @@ impl Feature<api::Capture, types::PaymentsCaptureData>
         call_connector_action: payments::CallConnectorAction,
         _merchant_account: &domain::MerchantAccount,
         connector_request: Option<services::Request>,
+        _key_store: &domain::MerchantKeyStore,
     ) -> RouterResult<Self> {
         let connector_integration: services::BoxedConnectorIntegration<
             '_,
diff --git a/crates/router/src/core/payments/flows/complete_authorize_flow.rs b/crates/router/src/core/payments/flows/complete_authorize_flow.rs
index 38ae919f498..1b6f163c1b9 100644
--- a/crates/router/src/core/payments/flows/complete_authorize_flow.rs
+++ b/crates/router/src/core/payments/flows/complete_authorize_flow.rs
@@ -65,6 +65,7 @@ impl Feature<api::CompleteAuthorize, types::CompleteAuthorizeData>
         call_connector_action: payments::CallConnectorAction,
         _merchant_account: &domain::MerchantAccount,
         connector_request: Option<services::Request>,
+        _key_store: &domain::MerchantKeyStore,
     ) -> RouterResult<Self> {
         let connector_integration: services::BoxedConnectorIntegration<
             '_,
diff --git a/crates/router/src/core/payments/flows/psync_flow.rs b/crates/router/src/core/payments/flows/psync_flow.rs
index f72cf897e0c..f39ee13395e 100644
--- a/crates/router/src/core/payments/flows/psync_flow.rs
+++ b/crates/router/src/core/payments/flows/psync_flow.rs
@@ -51,6 +51,7 @@ impl Feature<api::PSync, types::PaymentsSyncData>
         call_connector_action: payments::CallConnectorAction,
         _merchant_account: &domain::MerchantAccount,
         connector_request: Option<services::Request>,
+        _key_store: &domain::MerchantKeyStore,
     ) -> RouterResult<Self> {
         let connector_integration: services::BoxedConnectorIntegration<
             '_,
diff --git a/crates/router/src/core/payments/flows/reject_flow.rs b/crates/router/src/core/payments/flows/reject_flow.rs
index 1dcd6fd8892..64ab9e06de4 100644
--- a/crates/router/src/core/payments/flows/reject_flow.rs
+++ b/crates/router/src/core/payments/flows/reject_flow.rs
@@ -47,6 +47,7 @@ impl Feature<api::Reject, types::PaymentsRejectData>
         _call_connector_action: payments::CallConnectorAction,
         _merchant_account: &domain::MerchantAccount,
         _connector_request: Option<services::Request>,
+        _key_store: &domain::MerchantKeyStore,
     ) -> RouterResult<Self> {
         Err(ApiErrorResponse::NotImplemented {
             message: NotImplementedMessage::Reason("Flow not supported".to_string()),
diff --git a/crates/router/src/core/payments/flows/session_flow.rs b/crates/router/src/core/payments/flows/session_flow.rs
index 29265d0603d..f02e7bd1ddb 100644
--- a/crates/router/src/core/payments/flows/session_flow.rs
+++ b/crates/router/src/core/payments/flows/session_flow.rs
@@ -51,6 +51,7 @@ impl Feature<api::Session, types::PaymentsSessionData> for types::PaymentsSessio
         call_connector_action: payments::CallConnectorAction,
         _merchant_account: &domain::MerchantAccount,
         _connector_request: Option<services::Request>,
+        _key_store: &domain::MerchantKeyStore,
     ) -> RouterResult<Self> {
         metrics::SESSION_TOKEN_CREATED.add(
             &metrics::CONTEXT,
diff --git a/crates/router/src/core/payments/flows/verify_flow.rs b/crates/router/src/core/payments/flows/verify_flow.rs
index 00c47bbb59b..9e9af95a294 100644
--- a/crates/router/src/core/payments/flows/verify_flow.rs
+++ b/crates/router/src/core/payments/flows/verify_flow.rs
@@ -46,6 +46,7 @@ impl Feature<api::Verify, types::VerifyRequestData> for types::VerifyRouterData
         call_connector_action: payments::CallConnectorAction,
         merchant_account: &domain::MerchantAccount,
         connector_request: Option<services::Request>,
+        key_store: &domain::MerchantKeyStore,
     ) -> RouterResult<Self> {
         let connector_integration: services::BoxedConnectorIntegration<
             '_,
@@ -70,6 +71,7 @@ impl Feature<api::Verify, types::VerifyRequestData> for types::VerifyRouterData
             maybe_customer,
             merchant_account,
             self.request.payment_method_type,
+            key_store,
         )
         .await?;
 
@@ -156,6 +158,7 @@ impl TryFrom<types::VerifyRequestData> for types::ConnectorCustomerData {
     }
 }
 
+#[allow(clippy::too_many_arguments)]
 impl types::VerifyRouterData {
     pub async fn decide_flow<'a, 'b>(
         &'b self,
@@ -165,6 +168,7 @@ impl types::VerifyRouterData {
         confirm: Option<bool>,
         call_connector_action: payments::CallConnectorAction,
         merchant_account: &domain::MerchantAccount,
+        key_store: &domain::MerchantKeyStore,
     ) -> RouterResult<Self> {
         match confirm {
             Some(true) => {
@@ -192,6 +196,7 @@ impl types::VerifyRouterData {
                     maybe_customer,
                     merchant_account,
                     payment_method_type,
+                    key_store,
                 )
                 .await?;
 
diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs
index c38cb4a48d0..e50883ccffa 100644
--- a/crates/router/src/core/payments/tokenization.rs
+++ b/crates/router/src/core/payments/tokenization.rs
@@ -13,7 +13,7 @@ use crate::{
     services,
     types::{
         self,
-        api::{self, PaymentMethodCreateExt},
+        api::{self, CardDetailsPaymentMethod, PaymentMethodCreateExt},
         domain,
         storage::enums as storage_enums,
     },
@@ -27,6 +27,7 @@ pub async fn save_payment_method<F: Clone, FData>(
     maybe_customer: &Option<domain::Customer>,
     merchant_account: &domain::MerchantAccount,
     payment_method_type: Option<storage_enums::PaymentMethodType>,
+    key_store: &domain::MerchantKeyStore,
 ) -> RouterResult<Option<String>>
 where
     FData: mandate::MandateBehaviour,
@@ -71,6 +72,19 @@ where
                 .await?;
                 let is_duplicate = locker_response.1;
 
+                let pm_card_details = locker_response.0.card.as_ref().map(|card| {
+                    api::payment_methods::PaymentMethodsData::Card(CardDetailsPaymentMethod::from(
+                        card.clone(),
+                    ))
+                });
+
+                let pm_data_encrypted =
+                    payment_methods::cards::create_encrypted_payment_method_data(
+                        key_store,
+                        pm_card_details,
+                    )
+                    .await;
+
                 if is_duplicate {
                     let existing_pm = db
                         .find_payment_method(&locker_response.0.payment_method_id)
@@ -103,6 +117,7 @@ where
                                             &locker_response.0.payment_method_id,
                                             merchant_id,
                                             pm_metadata,
+                                            pm_data_encrypted,
                                         )
                                         .await
                                         .change_context(
@@ -131,6 +146,7 @@ where
                         &locker_response.0.payment_method_id,
                         merchant_id,
                         pm_metadata,
+                        pm_data_encrypted,
                     )
                     .await
                     .change_context(errors::ApiErrorResponse::InternalServerError)
diff --git a/crates/router/src/core/payouts/helpers.rs b/crates/router/src/core/payouts/helpers.rs
index 6959962a13e..90eef962c9e 100644
--- a/crates/router/src/core/payouts/helpers.rs
+++ b/crates/router/src/core/payouts/helpers.rs
@@ -209,6 +209,24 @@ pub async fn save_payout_data_to_locker(
     .change_context(errors::ApiErrorResponse::InternalServerError)
     .attach_printable("Error updating payouts in saved payout method")?;
 
+    let pm_data = api::payment_methods::PaymentMethodsData::Card(
+        api::payment_methods::CardDetailsPaymentMethod {
+            last4_digits: card_details
+                .as_ref()
+                .map(|c| c.card_number.clone().get_last4()),
+            issuer_country: None,
+            expiry_month: card_details.as_ref().map(|c| c.card_exp_month.clone()),
+            expiry_year: card_details.as_ref().map(|c| c.card_exp_year.clone()),
+            nick_name: card_details.as_ref().and_then(|c| c.nick_name.clone()),
+            card_holder_name: card_details
+                .as_ref()
+                .and_then(|c| c.card_holder_name.clone()),
+        },
+    );
+
+    let card_details_encrypted =
+        cards::create_encrypted_payment_method_data(key_store, Some(pm_data)).await;
+
     // Insert in payment_method table
     let payment_method = api::PaymentMethodCreate {
         payment_method: api_enums::PaymentMethod::foreign_from(payout_method_data.to_owned()),
@@ -220,6 +238,7 @@ pub async fn save_payout_data_to_locker(
         customer_id: Some(payout_attempt.customer_id.to_owned()),
         card_network: None,
     };
+
     cards::create_payment_method(
         db,
         &payment_method,
@@ -227,6 +246,7 @@ pub async fn save_payout_data_to_locker(
         &stored_resp.card_reference,
         &merchant_account.merchant_id,
         None,
+        card_details_encrypted,
     )
     .await
     .change_context(errors::ApiErrorResponse::InternalServerError)
diff --git a/crates/router/src/db/payment_method.rs b/crates/router/src/db/payment_method.rs
index 719d6afcd77..dd7cbedd0f4 100644
--- a/crates/router/src/db/payment_method.rs
+++ b/crates/router/src/db/payment_method.rs
@@ -159,6 +159,7 @@ impl PaymentMethodInterface for MockDb {
             payment_method_issuer: payment_method_new.payment_method_issuer,
             payment_method_issuer_code: payment_method_new.payment_method_issuer_code,
             metadata: payment_method_new.metadata,
+            payment_method_data: payment_method_new.payment_method_data,
         };
         payment_methods.push(payment_method.clone());
         Ok(payment_method)
diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs
index 1024ef0092e..c828c045683 100644
--- a/crates/router/src/routes/payment_methods.rs
+++ b/crates/router/src/routes/payment_methods.rs
@@ -40,7 +40,7 @@ pub async fn create_payment_method_api(
         &req,
         json_payload.into_inner(),
         |state, auth, req| async move {
-            cards::add_payment_method(state, req, &auth.merchant_account).await
+            cards::add_payment_method(state, req, &auth.merchant_account, &auth.key_store).await
         },
         &auth::ApiKeyAuth,
     )
@@ -289,6 +289,7 @@ pub async fn payment_method_update_api(
                 auth.merchant_account,
                 payload,
                 &payment_method_id,
+                auth.key_store,
             )
         },
         &auth::ApiKeyAuth,
diff --git a/crates/router/src/types/api/payment_methods.rs b/crates/router/src/types/api/payment_methods.rs
index 33687b67fa6..e5bf1d8dd1b 100644
--- a/crates/router/src/types/api/payment_methods.rs
+++ b/crates/router/src/types/api/payment_methods.rs
@@ -1,11 +1,12 @@
 use api_models::enums as api_enums;
 pub use api_models::payment_methods::{
-    CardDetail, CardDetailFromLocker, CustomerPaymentMethod, CustomerPaymentMethodsListResponse,
-    DeleteTokenizeByDateRequest, DeleteTokenizeByTokenRequest, GetTokenizePayloadRequest,
-    GetTokenizePayloadResponse, PaymentMethodCreate, PaymentMethodDeleteResponse, PaymentMethodId,
-    PaymentMethodList, PaymentMethodListRequest, PaymentMethodListResponse, PaymentMethodResponse,
-    PaymentMethodUpdate, TokenizePayloadEncrypted, TokenizePayloadRequest, TokenizedCardValue1,
-    TokenizedCardValue2, TokenizedWalletValue1, TokenizedWalletValue2,
+    CardDetail, CardDetailFromLocker, CardDetailsPaymentMethod, CustomerPaymentMethod,
+    CustomerPaymentMethodsListResponse, DeleteTokenizeByDateRequest, DeleteTokenizeByTokenRequest,
+    GetTokenizePayloadRequest, GetTokenizePayloadResponse, PaymentMethodCreate,
+    PaymentMethodDeleteResponse, PaymentMethodId, PaymentMethodList, PaymentMethodListRequest,
+    PaymentMethodListResponse, PaymentMethodResponse, PaymentMethodUpdate, PaymentMethodsData,
+    TokenizePayloadEncrypted, TokenizePayloadRequest, TokenizedCardValue1, TokenizedCardValue2,
+    TokenizedWalletValue1, TokenizedWalletValue2,
 };
 use error_stack::report;
 
diff --git a/migrations/2023-09-06-101704_payment_method_data_in_payment_methods/down.sql b/migrations/2023-09-06-101704_payment_method_data_in_payment_methods/down.sql
new file mode 100644
index 00000000000..056bc6a56f8
--- /dev/null
+++ b/migrations/2023-09-06-101704_payment_method_data_in_payment_methods/down.sql
@@ -0,0 +1,2 @@
+-- This file should undo anything in `up.sql`
+ALTER TABLE payment_methods DROP COLUMN IF EXISTS payment_method_data;
\ No newline at end of file
diff --git a/migrations/2023-09-06-101704_payment_method_data_in_payment_methods/up.sql b/migrations/2023-09-06-101704_payment_method_data_in_payment_methods/up.sql
new file mode 100644
index 00000000000..73ea61531b6
--- /dev/null
+++ b/migrations/2023-09-06-101704_payment_method_data_in_payment_methods/up.sql
@@ -0,0 +1,2 @@
+-- Your SQL goes here
+ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS payment_method_data BYTEA DEFAULT NULL;
\ No newline at end of file

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

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

## Description
<!-- Describe your changes in detail -->
Currently, The Customer Payment method List API resorts to a locker call to fetch card details. The latency induced from this is reduced by storing encrypted card details in payment_methods table. This PR does that.

### Additional Changes

- [ ] This PR modifies the API contract
- [x] 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)?
-->
Payments Create - `curl --location --request POST 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_Ia7M1EapArhTiWcTqmQMCLJ4O8lXRbSz8vrxd2nTAZMgOIkBttrLRXFY6owwhlL7' \
--data-raw '{
  "amount": 6540,
  "currency": "USD",
  "confirm": true,
  "capture_method": "automatic",
  "capture_on": "2022-09-10T10:11:12Z",
  "amount_to_capture": 6540,
  "customer_id": "StripeCustomer3",
  "email": "guest@example.com",
  "name": "John Doe",
  "phone": "999999999",
  "phone_country_code": "+1",
  "description": "Its my first payment request",
  "authentication_type": "no_three_ds",
  "return_url": "https://google.com",
  "payment_method": "card",
  "payment_method_type": "credit",
  "setup_future_usage":"off_session",
  "payment_method_data": {
    "card": {
      "card_number": "4242424242424242",
      "card_exp_month": "10",
      "card_exp_year": "25",
      "card_holder_name": "joseph Doe",
      "card_cvc": "124",
      "card_network": "Visa"
    }
  },
  "billing": {
    "address": {
      "line1": "1467",
      "line2": "Harrison Street",
      "line3": "Harrison Street",
      "city": "San Fransico",
      "state": "California",
      "zip": "94122",
      "country": "US",
      "first_name": "john",
      "last_name": "Doe"
    },
    "phone": {
      "number": "8056594427",
      "country_code": "+91"
    }
  },
  "shipping": {
    "address": {
      "line1": "1467",
      "line2": "Harrison Street",
      "line3": "Harrison Street",
      "city": "San Fransico",
      "state": "California",
      "zip": "94122",
      "country": "US",
      "first_name": "joseph",
      "last_name": "Doe"
    },
    "phone": {
      "number": "8056594430",
      "country_code": "+91"
    }
  },
  "statement_descriptor_name": "joseph",
  "statement_descriptor_suffix": "JS",
  "metadata": {
    "udf1": "value1",
    "new_customer": "true",
    "login_date": "2019-09-10T10:11:12Z"
  }
}'`

List Payment methods for customers - `curl --location --request GET 'http://localhost:8080/customers/StripeCustomer1/payment_methods' \
--header 'Accept: application/json' \
--header 'api-key: dev_Ia7M1EapArhTiWcTqmQMCLJ4O8lXRbSz8vrxd2nTAZMgOIkBttrLRXFY6owwhlL7'`


<img width="1512" alt="image" src="https://github.com/juspay/hyperswitch/assets/76486416/fc5ae6f6-9d0e-4334-8519-b034957c41d7">
<img width="1512" alt="image" src="https://github.com/juspay/hyperswitch/assets/76486416/e50b678d-e3db-4980-8993-b5a35c8b6b6a">
<img width="1512" alt="image" src="https://github.com/juspay/hyperswitch/assets/76486416/e04eab03-de04-41a8-9d0c-5701a8d11b2f">



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