Bug: Refactor Customers Payment Method List to support Client as well as S2S calls



### Feature Description

For Payment Methods v2, the customers payment method list will be supporting client based as well as S2S calls.
We are moving it under two distinct endpoints.

### Possible Implementation

The customers Payment method list will be moved under two distinct endpoints: 
- For Client (with payment tokens) - `/payments/:id:/saved_payment_methods`
- For Server (without payment token) - `/customers/:cust_id:/saved_payment_methods`

Payment tokens for the listed payment methods would be generated only in case of client calls in a payments context.

diff --git a/crates/api_models/Cargo.toml b/crates/api_models/Cargo.toml
index f829d2e48fe..e2b9e2d3a55 100644
--- a/crates/api_models/Cargo.toml
+++ b/crates/api_models/Cargo.toml
@@ -24,6 +24,7 @@ customer_v2 = []
 merchant_account_v2 = []
 merchant_connector_account_v2 = []
 payment_v2 = []
+payment_methods_v2 = []
 routing_v2 = []
 
 [dependencies]
diff --git a/crates/api_models/src/events/payment.rs b/crates/api_models/src/events/payment.rs
index ad2e2546f37..589dcd8d10b 100644
--- a/crates/api_models/src/events/payment.rs
+++ b/crates/api_models/src/events/payment.rs
@@ -1,12 +1,19 @@
 use common_utils::events::{ApiEventMetric, ApiEventsType};
 
+#[cfg(all(
+    any(feature = "v2", feature = "v1"),
+    not(feature = "payment_methods_v2")
+))]
+use crate::payment_methods::CustomerPaymentMethodsListResponse;
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+use crate::payment_methods::CustomerPaymentMethodsListResponse;
 use crate::{
     payment_methods::{
-        CustomerDefaultPaymentMethodResponse, CustomerPaymentMethodsListResponse,
-        DefaultPaymentMethod, ListCountriesCurrenciesRequest, ListCountriesCurrenciesResponse,
-        PaymentMethodCollectLinkRenderRequest, PaymentMethodCollectLinkRequest,
-        PaymentMethodCollectLinkResponse, PaymentMethodDeleteResponse, PaymentMethodListRequest,
-        PaymentMethodListResponse, PaymentMethodResponse, PaymentMethodUpdate,
+        CustomerDefaultPaymentMethodResponse, DefaultPaymentMethod, ListCountriesCurrenciesRequest,
+        ListCountriesCurrenciesResponse, PaymentMethodCollectLinkRenderRequest,
+        PaymentMethodCollectLinkRequest, PaymentMethodCollectLinkResponse,
+        PaymentMethodDeleteResponse, PaymentMethodListRequest, PaymentMethodListResponse,
+        PaymentMethodResponse, PaymentMethodUpdate,
     },
     payments::{
         ExtendedCardInfoResponse, PaymentIdType, PaymentListConstraints,
diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs
index 3f1df4b8e96..c90031dc077 100644
--- a/crates/api_models/src/payment_methods.rs
+++ b/crates/api_models/src/payment_methods.rs
@@ -994,6 +994,19 @@ impl serde::Serialize for PaymentMethodList {
     }
 }
 
+#[cfg(all(
+    any(feature = "v2", feature = "v1"),
+    not(feature = "payment_methods_v2")
+))]
+#[derive(Debug, serde::Serialize, ToSchema)]
+pub struct CustomerPaymentMethodsListResponse {
+    /// List of payment methods for customer
+    pub customer_payment_methods: Vec<CustomerPaymentMethod>,
+    /// Returns whether a customer id is not tied to a payment intent (only when the request is made against a client secret)
+    pub is_guest_customer: Option<bool>,
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
 #[derive(Debug, serde::Serialize, ToSchema)]
 pub struct CustomerPaymentMethodsListResponse {
     /// List of payment methods for customer
@@ -1028,6 +1041,97 @@ pub struct CustomerDefaultPaymentMethodResponse {
     pub payment_method_type: Option<api_enums::PaymentMethodType>,
 }
 
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[derive(Debug, Clone, serde::Serialize, ToSchema)]
+pub struct CustomerPaymentMethod {
+    /// Token for payment method in temporary card locker which gets refreshed often
+    #[schema(example = "7ebf443f-a050-4067-84e5-e6f6d4800aef")]
+    pub payment_token: Option<String>,
+    /// The unique identifier of the customer.
+    #[schema(example = "pm_iouuy468iyuowqs")]
+    pub payment_method_id: String,
+
+    /// The unique identifier of the customer.
+    #[schema(value_type = String, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
+    pub customer_id: id_type::CustomerId,
+
+    /// The type of payment method use for the payment.
+    #[schema(value_type = PaymentMethod,example = "card")]
+    pub payment_method: api_enums::PaymentMethod,
+
+    /// This is a sub-category of payment method.
+    #[schema(value_type = Option<PaymentMethodType>,example = "credit_card")]
+    pub payment_method_type: Option<api_enums::PaymentMethodType>,
+
+    /// The name of the bank/ provider issuing the payment method to the end user
+    #[schema(example = "Citibank")]
+    pub payment_method_issuer: Option<String>,
+
+    /// A standard code representing the issuer of payment method
+    #[schema(value_type = Option<PaymentMethodIssuerCode>,example = "jp_applepay")]
+    pub payment_method_issuer_code: Option<api_enums::PaymentMethodIssuerCode>,
+
+    /// Indicates whether the payment method is eligible for recurring payments
+    #[schema(example = true)]
+    pub recurring_enabled: bool,
+
+    /// Indicates whether the payment method is eligible for installment payments
+    #[schema(example = true)]
+    pub installment_payment_enabled: bool,
+
+    /// Type of payment experience enabled with the connector
+    #[schema(value_type = Option<Vec<PaymentExperience>>,example = json!(["redirect_to_url"]))]
+    pub payment_experience: Option<Vec<api_enums::PaymentExperience>>,
+
+    /// PaymentMethod Data from locker
+    pub payment_method_data: Option<PaymentMethodListData>,
+
+    /// Masked bank details from PM auth services
+    #[schema(example = json!({"mask": "0000"}))]
+    pub bank: Option<MaskedBankDetails>,
+
+    /// You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. Metadata is useful for storing additional, structured information on an object.
+    #[schema(value_type = Option<Object>,example = json!({ "city": "NY", "unit": "245" }))]
+    pub metadata: Option<pii::SecretSerdeValue>,
+
+    ///  A timestamp (ISO 8601 code) that determines when the customer was created
+    #[schema(value_type = Option<PrimitiveDateTime>,example = "2023-01-18T11:04:09.922Z")]
+    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
+    pub created: Option<time::PrimitiveDateTime>,
+
+    /// Surcharge details for this saved card
+    pub surcharge_details: Option<SurchargeDetailsResponse>,
+
+    /// Whether this payment method requires CVV to be collected
+    #[schema(example = true)]
+    pub requires_cvv: bool,
+
+    ///  A timestamp (ISO 8601 code) that determines when the payment method was last used
+    #[schema(value_type = Option<PrimitiveDateTime>,example = "2024-02-24T11:04:09.922Z")]
+    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
+    pub last_used_at: Option<time::PrimitiveDateTime>,
+    /// Indicates if the payment method has been set to default or not
+    #[schema(example = true)]
+    pub default_payment_method_set: bool,
+
+    /// The billing details of the payment method
+    #[schema(value_type = Option<Address>)]
+    pub billing: Option<payments::Address>,
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[derive(Debug, Clone, serde::Serialize, ToSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum PaymentMethodListData {
+    Card(CardDetailFromLocker),
+    #[cfg(feature = "payouts")]
+    Bank(payouts::Bank),
+}
+
+#[cfg(all(
+    any(feature = "v2", feature = "v1"),
+    not(feature = "payment_methods_v2")
+))]
 #[derive(Debug, Clone, serde::Serialize, ToSchema)]
 pub struct CustomerPaymentMethod {
     /// Token for payment method in temporary card locker which gets refreshed often
diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml
index 3e6f470d80e..3492006261c 100644
--- a/crates/router/Cargo.toml
+++ b/crates/router/Cargo.toml
@@ -37,6 +37,7 @@ v1 = ["api_models/v1", "diesel_models/v1", "hyperswitch_domain_models/v1", "stor
 customer_v2 = ["api_models/customer_v2", "diesel_models/customer_v2", "hyperswitch_domain_models/customer_v2"]
 merchant_account_v2 = ["api_models/merchant_account_v2", "diesel_models/merchant_account_v2", "hyperswitch_domain_models/merchant_account_v2"]
 payment_v2 = ["api_models/payment_v2", "diesel_models/payment_v2", "hyperswitch_domain_models/payment_v2"]
+payment_methods_v2 = ["api_models/payment_methods_v2"]
 routing_v2 = ["api_models/routing_v2"]
 merchant_connector_account_v2 = ["api_models/merchant_connector_account_v2", "kgraph_utils/merchant_connector_account_v2", "hyperswitch_domain_models/merchant_connector_account_v2", "diesel_models/merchant_connector_account_v2"]
 
diff --git a/crates/router/src/compatibility/stripe/customers/types.rs b/crates/router/src/compatibility/stripe/customers/types.rs
index e2acc7b17b5..1dc43175342 100644
--- a/crates/router/src/compatibility/stripe/customers/types.rs
+++ b/crates/router/src/compatibility/stripe/customers/types.rs
@@ -193,7 +193,7 @@ pub struct CustomerPaymentMethodListResponse {
 
 #[derive(Default, Serialize, PartialEq, Eq)]
 pub struct PaymentMethodData {
-    pub id: String,
+    pub id: Option<String>,
     pub object: &'static str,
     pub card: Option<CardDetails>,
     pub created: Option<time::PrimitiveDateTime>,
@@ -222,12 +222,29 @@ impl From<api::CustomerPaymentMethodsListResponse> for CustomerPaymentMethodList
     }
 }
 
+// Check this in review
 impl From<api_types::CustomerPaymentMethod> for PaymentMethodData {
     fn from(item: api_types::CustomerPaymentMethod) -> Self {
+        #[cfg(all(
+            any(feature = "v1", feature = "v2"),
+            not(feature = "payment_methods_v2")
+        ))]
+        let card = item.card.map(From::from);
+        #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+        let card = match item.payment_method_data {
+            Some(api_types::PaymentMethodListData::Card(card)) => Some(CardDetails::from(card)),
+            _ => None,
+        };
         Self {
+            #[cfg(all(
+                any(feature = "v1", feature = "v2"),
+                not(feature = "payment_methods_v2")
+            ))]
+            id: Some(item.payment_token),
+            #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
             id: item.payment_token,
             object: "payment_method",
-            card: item.card.map(From::from),
+            card,
             created: item.created,
         }
     }
diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs
index 8cb64d1e0d8..f3bccf4d65f 100644
--- a/crates/router/src/core/payment_methods/cards.rs
+++ b/crates/router/src/core/payment_methods/cards.rs
@@ -48,6 +48,11 @@ use super::surcharge_decision_configs::{
     perform_surcharge_decision_management_for_payment_method_list,
     perform_surcharge_decision_management_for_saved_cards,
 };
+#[cfg(all(
+    any(feature = "v2", feature = "v1"),
+    not(feature = "payment_methods_v2")
+))]
+use crate::routes::app::SessionStateInfo;
 #[cfg(feature = "payouts")]
 use crate::types::domain::types::AsyncLift;
 use crate::{
@@ -67,7 +72,7 @@ use crate::{
     },
     db, logger,
     pii::prelude::*,
-    routes::{self, app::SessionStateInfo, metrics, payment_methods::ParentPaymentMethodToken},
+    routes::{self, metrics, payment_methods::ParentPaymentMethodToken},
     services,
     types::{
         api::{self, routing as routing_types, PaymentMethodCreateExt},
@@ -3609,6 +3614,63 @@ fn filter_recurring_based(
     recurring_enabled.map_or(true, |enabled| payment_method.recurring_enabled == enabled)
 }
 
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+pub async fn list_customer_payment_method_util(
+    state: routes::SessionState,
+    merchant_account: domain::MerchantAccount,
+    key_store: domain::MerchantKeyStore,
+    req: Option<api::PaymentMethodListRequest>,
+    customer_id: Option<id_type::CustomerId>,
+    is_payment_associated: bool,
+) -> errors::RouterResponse<api::CustomerPaymentMethodsListResponse> {
+    let limit = req.as_ref().and_then(|pml_req| pml_req.limit);
+
+    let (customer_id, payment_intent) = if is_payment_associated {
+        let cloned_secret = req.and_then(|r| r.client_secret.clone());
+        let payment_intent = helpers::verify_payment_intent_time_and_client_secret(
+            &state,
+            &merchant_account,
+            &key_store,
+            cloned_secret,
+        )
+        .await?;
+
+        (
+            payment_intent
+                .as_ref()
+                .and_then(|pi| pi.customer_id.clone()),
+            payment_intent,
+        )
+    } else {
+        (customer_id, None)
+    };
+
+    let resp = if let Some(cust) = customer_id {
+        Box::pin(list_customer_payment_method(
+            &state,
+            merchant_account,
+            key_store,
+            payment_intent,
+            &cust,
+            limit,
+            is_payment_associated,
+        ))
+        .await?
+    } else {
+        let response = api::CustomerPaymentMethodsListResponse {
+            customer_payment_methods: Vec::new(),
+            is_guest_customer: Some(true),
+        };
+        services::ApplicationResponse::Json(response)
+    };
+
+    Ok(resp)
+}
+
+#[cfg(all(
+    any(feature = "v2", feature = "v1"),
+    not(feature = "payment_methods_v2")
+))]
 pub async fn do_list_customer_pm_fetch_customer_if_not_passed(
     state: routes::SessionState,
     merchant_account: domain::MerchantAccount,
@@ -3680,6 +3742,10 @@ pub async fn do_list_customer_pm_fetch_customer_if_not_passed(
     }
 }
 
+#[cfg(all(
+    any(feature = "v2", feature = "v1"),
+    not(feature = "payment_methods_v2")
+))]
 pub async fn list_customer_payment_method(
     state: &routes::SessionState,
     merchant_account: domain::MerchantAccount,
@@ -3767,88 +3833,21 @@ pub async fn list_customer_payment_method(
 
         let payment_method = pm.payment_method.get_required_value("payment_method")?;
 
-        let payment_method_retrieval_context = match payment_method {
-            enums::PaymentMethod::Card => {
-                let card_details =
-                    get_card_details_with_locker_fallback(&pm, state, &key_store).await?;
-
-                if card_details.is_some() {
-                    PaymentMethodListContext {
-                        card_details,
-                        #[cfg(feature = "payouts")]
-                        bank_transfer_details: None,
-                        hyperswitch_token_data: PaymentTokenData::permanent_card(
-                            Some(pm.payment_method_id.clone()),
-                            pm.locker_id.clone().or(Some(pm.payment_method_id.clone())),
-                            pm.locker_id.clone().unwrap_or(pm.payment_method_id.clone()),
-                        ),
-                    }
-                } else {
-                    continue;
-                }
-            }
-
-            enums::PaymentMethod::BankDebit => {
-                // Retrieve the pm_auth connector details so that it can be tokenized
-                let bank_account_token_data =
-                    get_bank_account_connector_details(state, &pm, &key_store)
-                        .await
-                        .unwrap_or_else(|error| {
-                            logger::error!(?error);
-                            None
-                        });
-                if let Some(data) = bank_account_token_data {
-                    let token_data = PaymentTokenData::AuthBankDebit(data);
-
-                    PaymentMethodListContext {
-                        card_details: None,
-                        #[cfg(feature = "payouts")]
-                        bank_transfer_details: None,
-                        hyperswitch_token_data: token_data,
-                    }
-                } else {
-                    continue;
-                }
-            }
-
-            enums::PaymentMethod::Wallet => PaymentMethodListContext {
-                card_details: None,
-                #[cfg(feature = "payouts")]
-                bank_transfer_details: None,
-                hyperswitch_token_data: PaymentTokenData::wallet_token(
-                    pm.payment_method_id.clone(),
-                ),
-            },
+        let pm_list_context = get_pm_list_context(
+            state,
+            &payment_method,
+            &key_store,
+            &pm,
+            Some(parent_payment_method_token.clone()),
+            true,
+        )
+        .await?;
 
-            #[cfg(feature = "payouts")]
-            enums::PaymentMethod::BankTransfer => PaymentMethodListContext {
-                card_details: None,
-                bank_transfer_details: Some(
-                    get_bank_from_hs_locker(
-                        state,
-                        &key_store,
-                        &parent_payment_method_token,
-                        &pm.customer_id,
-                        &pm.merchant_id,
-                        pm.locker_id.as_ref().unwrap_or(&pm.payment_method_id),
-                    )
-                    .await?,
-                ),
-                hyperswitch_token_data: PaymentTokenData::temporary_generic(
-                    parent_payment_method_token.clone(),
-                ),
-            },
+        if pm_list_context.is_none() {
+            continue;
+        }
 
-            _ => PaymentMethodListContext {
-                card_details: None,
-                #[cfg(feature = "payouts")]
-                bank_transfer_details: None,
-                hyperswitch_token_data: PaymentTokenData::temporary_generic(generate_id(
-                    consts::ID_LENGTH,
-                    "token",
-                )),
-            },
-        };
+        let pm_list_context = pm_list_context.get_required_value("PaymentMethodListContext")?;
 
         // Retrieve the masked bank details to be sent as a response
         let bank_details = if payment_method == enums::PaymentMethod::BankDebit {
@@ -3904,7 +3903,7 @@ pub async fn list_customer_payment_method(
             payment_method,
             payment_method_type: pm.payment_method_type,
             payment_method_issuer: pm.payment_method_issuer,
-            card: payment_method_retrieval_context.card_details,
+            card: pm_list_context.card_details,
             metadata: pm.metadata,
             payment_method_issuer_code: pm.payment_method_issuer_code,
             recurring_enabled: mca_enabled,
@@ -3912,7 +3911,7 @@ pub async fn list_customer_payment_method(
             payment_experience: Some(vec![api_models::enums::PaymentExperience::RedirectToUrl]),
             created: Some(pm.created_at),
             #[cfg(feature = "payouts")]
-            bank_transfer: payment_method_retrieval_context.bank_transfer_details,
+            bank_transfer: pm_list_context.bank_transfer_details,
             bank: bank_details,
             surcharge_details: None,
             requires_cvv,
@@ -3934,15 +3933,15 @@ pub async fn list_customer_payment_method(
             .and_then(|b_profile| b_profile.intent_fulfillment_time)
             .unwrap_or(consts::DEFAULT_INTENT_FULFILLMENT_TIME);
 
+        let hyperswitch_token_data = pm_list_context
+            .hyperswitch_token_data
+            .get_required_value("PaymentTokenData")?;
+
         ParentPaymentMethodToken::create_key_for_token((
             &parent_payment_method_token,
             pma.payment_method,
         ))
-        .insert(
-            intent_fulfillment_time,
-            payment_method_retrieval_context.hyperswitch_token_data,
-            state,
-        )
+        .insert(intent_fulfillment_time, hyperswitch_token_data, state)
         .await?;
 
         if let Some(metadata) = pma.metadata {
@@ -3973,6 +3972,115 @@ pub async fn list_customer_payment_method(
         customer_payment_methods: customer_pms,
         is_guest_customer: payment_intent.as_ref().map(|_| false), //to return this key only when the request is tied to a payment intent
     };
+
+    Box::pin(perform_surcharge_ops(
+        payment_intent,
+        state,
+        merchant_account,
+        key_store,
+        business_profile,
+        &mut response,
+    ))
+    .await?;
+
+    Ok(services::ApplicationResponse::Json(response))
+}
+
+async fn get_pm_list_context(
+    state: &routes::SessionState,
+    payment_method: &enums::PaymentMethod,
+    key_store: &domain::MerchantKeyStore,
+    pm: &diesel_models::PaymentMethod,
+    #[cfg(feature = "payouts")] parent_payment_method_token: Option<String>,
+    #[cfg(not(feature = "payouts"))] _parent_payment_method_token: Option<String>,
+    is_payment_associated: bool,
+) -> Result<Option<PaymentMethodListContext>, error_stack::Report<errors::ApiErrorResponse>> {
+    let payment_method_retrieval_context = match payment_method {
+        enums::PaymentMethod::Card => {
+            let card_details = get_card_details_with_locker_fallback(pm, state, key_store).await?;
+
+            card_details.as_ref().map(|card| PaymentMethodListContext {
+                card_details: Some(card.clone()),
+                #[cfg(feature = "payouts")]
+                bank_transfer_details: None,
+                hyperswitch_token_data: is_payment_associated.then_some(
+                    PaymentTokenData::permanent_card(
+                        Some(pm.payment_method_id.clone()),
+                        pm.locker_id.clone().or(Some(pm.payment_method_id.clone())),
+                        pm.locker_id.clone().unwrap_or(pm.payment_method_id.clone()),
+                    ),
+                ),
+            })
+        }
+
+        enums::PaymentMethod::BankDebit => {
+            // Retrieve the pm_auth connector details so that it can be tokenized
+            let bank_account_token_data = get_bank_account_connector_details(state, pm, key_store)
+                .await
+                .unwrap_or_else(|err| {
+                    logger::error!(error=?err);
+                    None
+                });
+
+            bank_account_token_data.map(|data| {
+                let token_data = PaymentTokenData::AuthBankDebit(data);
+
+                PaymentMethodListContext {
+                    card_details: None,
+                    #[cfg(feature = "payouts")]
+                    bank_transfer_details: None,
+                    hyperswitch_token_data: is_payment_associated.then_some(token_data),
+                }
+            })
+        }
+
+        enums::PaymentMethod::Wallet => Some(PaymentMethodListContext {
+            card_details: None,
+            #[cfg(feature = "payouts")]
+            bank_transfer_details: None,
+            hyperswitch_token_data: is_payment_associated
+                .then_some(PaymentTokenData::wallet_token(pm.payment_method_id.clone())),
+        }),
+
+        #[cfg(feature = "payouts")]
+        enums::PaymentMethod::BankTransfer => Some(PaymentMethodListContext {
+            card_details: None,
+            bank_transfer_details: Some(
+                get_bank_from_hs_locker(
+                    state,
+                    key_store,
+                    parent_payment_method_token.as_ref(),
+                    &pm.customer_id,
+                    &pm.merchant_id,
+                    pm.locker_id.as_ref().unwrap_or(&pm.payment_method_id),
+                )
+                .await?,
+            ),
+            hyperswitch_token_data: parent_payment_method_token
+                .map(|token| PaymentTokenData::temporary_generic(token.clone())),
+        }),
+
+        _ => Some(PaymentMethodListContext {
+            card_details: None,
+            #[cfg(feature = "payouts")]
+            bank_transfer_details: None,
+            hyperswitch_token_data: is_payment_associated.then_some(
+                PaymentTokenData::temporary_generic(generate_id(consts::ID_LENGTH, "token")),
+            ),
+        }),
+    };
+
+    Ok(payment_method_retrieval_context)
+}
+
+async fn perform_surcharge_ops(
+    payment_intent: Option<storage::PaymentIntent>,
+    state: &routes::SessionState,
+    merchant_account: domain::MerchantAccount,
+    key_store: domain::MerchantKeyStore,
+    business_profile: Option<BusinessProfile>,
+    response: &mut api::CustomerPaymentMethodsListResponse,
+) -> Result<(), error_stack::Report<errors::ApiErrorResponse>> {
     let payment_attempt = payment_intent
         .as_ref()
         .async_map(|payment_intent| async {
@@ -3989,7 +4097,6 @@ pub async fn list_customer_payment_method(
         })
         .await
         .transpose()?;
-
     if let Some((payment_attempt, payment_intent, business_profile)) = payment_attempt
         .zip(payment_intent)
         .zip(business_profile)
@@ -4002,13 +4109,355 @@ pub async fn list_customer_payment_method(
             &business_profile,
             &payment_attempt,
             payment_intent,
-            &mut response,
+            response,
+        )
+        .await?;
+    }
+
+    Ok(())
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+struct SavedPMLPaymentsInfo {
+    pub payment_intent: storage::PaymentIntent,
+    pub business_profile: Option<BusinessProfile>,
+    pub requires_cvv: bool,
+    pub off_session_payment_flag: bool,
+    pub is_connector_agnostic_mit_enabled: bool,
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+impl SavedPMLPaymentsInfo {
+    pub async fn form_payments_info(
+        payment_intent: storage::PaymentIntent,
+        merchant_account: &domain::MerchantAccount,
+        db: &dyn db::StorageInterface,
+    ) -> errors::RouterResult<Self> {
+        let requires_cvv = db
+            .find_config_by_key_unwrap_or(
+                format!("{}_requires_cvv", merchant_account.get_id()).as_str(),
+                Some("true".to_string()),
+            )
+            .await
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("Failed to fetch requires_cvv config")?
+            .config
+            != "false";
+
+        let off_session_payment_flag = matches!(
+            payment_intent.setup_future_usage,
+            Some(common_enums::FutureUsage::OffSession)
+        );
+
+        let profile_id = core_utils::get_profile_id_from_business_details(
+            payment_intent.business_country,
+            payment_intent.business_label.as_ref(),
+            merchant_account,
+            payment_intent.profile_id.as_ref(),
+            db,
+            false,
+        )
+        .await
+        .attach_printable("Could not find profile id from business details")?;
+
+        let business_profile = core_utils::validate_and_get_business_profile(
+            db,
+            Some(profile_id).as_ref(),
+            merchant_account.get_id(),
         )
         .await?;
+
+        let is_connector_agnostic_mit_enabled = business_profile
+            .as_ref()
+            .and_then(|business_profile| business_profile.is_connector_agnostic_mit_enabled)
+            .unwrap_or(false);
+
+        Ok(Self {
+            payment_intent,
+            business_profile,
+            requires_cvv,
+            off_session_payment_flag,
+            is_connector_agnostic_mit_enabled,
+        })
+    }
+
+    pub async fn perform_payment_ops(
+        &self,
+        state: &routes::SessionState,
+        parent_payment_method_token: Option<String>,
+        pma: &api::CustomerPaymentMethod,
+        pm_list_context: PaymentMethodListContext,
+    ) -> errors::RouterResult<()> {
+        let token = parent_payment_method_token
+            .as_ref()
+            .get_required_value("parent_payment_method_token")?;
+        let hyperswitch_token_data = pm_list_context
+            .hyperswitch_token_data
+            .get_required_value("PaymentTokenData")?;
+
+        let intent_fulfillment_time = self
+            .business_profile
+            .as_ref()
+            .and_then(|b_profile| b_profile.intent_fulfillment_time)
+            .unwrap_or(consts::DEFAULT_INTENT_FULFILLMENT_TIME);
+
+        ParentPaymentMethodToken::create_key_for_token((token, pma.payment_method))
+            .insert(intent_fulfillment_time, hyperswitch_token_data, state)
+            .await?;
+
+        if let Some(metadata) = pma.metadata.clone() {
+            let pm_metadata_vec: payment_methods::PaymentMethodMetadata = metadata
+                .parse_value("PaymentMethodMetadata")
+                .change_context(errors::ApiErrorResponse::InternalServerError)
+                .attach_printable(
+                    "Failed to deserialize metadata to PaymentmethodMetadata struct",
+                )?;
+
+            let redis_conn = state
+                .store
+                .get_redis_conn()
+                .change_context(errors::ApiErrorResponse::InternalServerError)
+                .attach_printable("Failed to get redis connection")?;
+
+            for pm_metadata in pm_metadata_vec.payment_method_tokenization {
+                let key = format!(
+                    "pm_token_{}_{}_{}",
+                    token, pma.payment_method, pm_metadata.0
+                );
+
+                redis_conn
+                    .set_key_with_expiry(&key, pm_metadata.1, intent_fulfillment_time)
+                    .await
+                    .change_context(errors::StorageError::KVError)
+                    .change_context(errors::ApiErrorResponse::InternalServerError)
+                    .attach_printable("Failed to add data in redis")?;
+            }
+        }
+
+        Ok(())
+    }
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+pub async fn list_customer_payment_method(
+    state: &routes::SessionState,
+    merchant_account: domain::MerchantAccount,
+    key_store: domain::MerchantKeyStore,
+    payment_intent: Option<storage::PaymentIntent>,
+    customer_id: &id_type::CustomerId,
+    limit: Option<i64>,
+    is_payment_associated: bool,
+) -> errors::RouterResponse<api::CustomerPaymentMethodsListResponse> {
+    let db = &*state.store;
+    let key_manager_state = &(state).into();
+    // let key = key_store.key.get_inner().peek();
+
+    let customer = db
+        .find_customer_by_customer_id_merchant_id(
+            key_manager_state,
+            customer_id,
+            merchant_account.get_id(),
+            &key_store,
+            merchant_account.storage_scheme,
+        )
+        .await
+        .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?;
+
+    let payments_info = payment_intent
+        .async_map(|pi| SavedPMLPaymentsInfo::form_payments_info(pi, &merchant_account, db))
+        .await
+        .transpose()?;
+
+    let saved_payment_methods = db
+        .find_payment_method_by_customer_id_merchant_id_status(
+            customer_id,
+            merchant_account.get_id(),
+            common_enums::PaymentMethodStatus::Active,
+            limit,
+            merchant_account.storage_scheme,
+        )
+        .await
+        .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;
+
+    let mut filtered_saved_payment_methods_ctx = Vec::new();
+    for pm in saved_payment_methods.into_iter() {
+        let payment_method = pm.payment_method.get_required_value("payment_method")?;
+        let parent_payment_method_token =
+            is_payment_associated.then(|| generate_id(consts::ID_LENGTH, "token"));
+
+        let pm_list_context = get_pm_list_context(
+            state,
+            &payment_method,
+            &key_store,
+            &pm,
+            parent_payment_method_token.clone(),
+            is_payment_associated,
+        )
+        .await?;
+
+        if let Some(ctx) = pm_list_context {
+            filtered_saved_payment_methods_ctx.push((ctx, parent_payment_method_token, pm));
+        }
+    }
+
+    let pm_list_futures = filtered_saved_payment_methods_ctx
+        .into_iter()
+        .map(|ctx| {
+            generate_saved_pm_response(
+                state,
+                &key_store,
+                &merchant_account,
+                ctx,
+                &customer,
+                payments_info.as_ref(),
+            )
+        })
+        .collect::<Vec<_>>();
+
+    let final_result = futures::future::join_all(pm_list_futures).await;
+
+    let mut customer_pms = Vec::new();
+    for result in final_result.into_iter() {
+        let pma = result.attach_printable("saved pm list failed")?;
+        customer_pms.push(pma);
+    }
+
+    let mut response = api::CustomerPaymentMethodsListResponse {
+        customer_payment_methods: customer_pms,
+        is_guest_customer: is_payment_associated.then_some(false), //to return this key only when the request is tied to a payment intent
+    };
+
+    if is_payment_associated {
+        Box::pin(perform_surcharge_ops(
+            payments_info.as_ref().map(|pi| pi.payment_intent.clone()),
+            state,
+            merchant_account,
+            key_store,
+            payments_info.and_then(|pi| pi.business_profile),
+            &mut response,
+        ))
+        .await?;
     }
 
     Ok(services::ApplicationResponse::Json(response))
 }
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+async fn generate_saved_pm_response(
+    state: &routes::SessionState,
+    key_store: &domain::MerchantKeyStore,
+    merchant_account: &domain::MerchantAccount,
+    pm_list_context: (
+        PaymentMethodListContext,
+        Option<String>,
+        diesel_models::PaymentMethod,
+    ),
+    customer: &domain::Customer,
+    payment_info: Option<&SavedPMLPaymentsInfo>,
+) -> Result<api::CustomerPaymentMethod, error_stack::Report<errors::ApiErrorResponse>> {
+    let (pm_list_context, parent_payment_method_token, pm) = pm_list_context;
+    let payment_method = pm.payment_method.get_required_value("payment_method")?;
+
+    let bank_details = if payment_method == enums::PaymentMethod::BankDebit {
+        get_masked_bank_details(state, &pm, key_store)
+            .await
+            .unwrap_or_else(|err| {
+                logger::error!(error=?err);
+                None
+            })
+    } else {
+        None
+    };
+
+    let payment_method_billing = decrypt_generic_data::<api_models::payments::Address>(
+        state,
+        pm.payment_method_billing_address,
+        key_store,
+    )
+    .await
+    .attach_printable("unable to decrypt payment method billing address details")?;
+
+    let connector_mandate_details = pm
+        .connector_mandate_details
+        .clone()
+        .map(|val| val.parse_value::<storage::PaymentsMandateReference>("PaymentsMandateReference"))
+        .transpose()
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Failed to deserialize to Payment Mandate Reference ")?;
+
+    let (is_connector_agnostic_mit_enabled, requires_cvv, off_session_payment_flag) = payment_info
+        .map(|pi| {
+            (
+                pi.is_connector_agnostic_mit_enabled,
+                pi.requires_cvv,
+                pi.off_session_payment_flag,
+            )
+        })
+        .unwrap_or((false, false, false));
+
+    let mca_enabled = get_mca_status(
+        state,
+        key_store,
+        merchant_account.get_id(),
+        is_connector_agnostic_mit_enabled,
+        connector_mandate_details,
+        pm.network_transaction_id.as_ref(),
+    )
+    .await?;
+
+    let requires_cvv = if is_connector_agnostic_mit_enabled {
+        requires_cvv
+            && !(off_session_payment_flag
+                && (pm.connector_mandate_details.is_some() || pm.network_transaction_id.is_some()))
+    } else {
+        requires_cvv && !(off_session_payment_flag && pm.connector_mandate_details.is_some())
+    };
+
+    let pmd = if let Some(card) = pm_list_context.card_details.as_ref() {
+        Some(api::PaymentMethodListData::Card(card.clone()))
+    } else if cfg!(feature = "payouts") {
+        pm_list_context
+            .bank_transfer_details
+            .clone()
+            .map(api::PaymentMethodListData::Bank)
+    } else {
+        None
+    };
+
+    let pma = api::CustomerPaymentMethod {
+        payment_token: parent_payment_method_token.clone(),
+        payment_method_id: pm.payment_method_id.clone(),
+        customer_id: pm.customer_id,
+        payment_method,
+        payment_method_type: pm.payment_method_type,
+        payment_method_issuer: pm.payment_method_issuer,
+        payment_method_data: pmd,
+        metadata: pm.metadata,
+        payment_method_issuer_code: pm.payment_method_issuer_code,
+        recurring_enabled: mca_enabled,
+        installment_payment_enabled: false,
+        payment_experience: Some(vec![api_models::enums::PaymentExperience::RedirectToUrl]),
+        created: Some(pm.created_at),
+        bank: bank_details,
+        surcharge_details: None,
+        requires_cvv: requires_cvv
+            && !(off_session_payment_flag && pm.connector_mandate_details.is_some()),
+        last_used_at: Some(pm.last_used_at),
+        default_payment_method_set: customer.default_payment_method_id.is_some()
+            && customer.default_payment_method_id == Some(pm.payment_method_id),
+        billing: payment_method_billing,
+    };
+
+    payment_info
+        .async_map(|pi| {
+            pi.perform_payment_ops(state, parent_payment_method_token, &pma, pm_list_context)
+        })
+        .await
+        .transpose()?;
+
+    Ok(pma)
+}
+
 pub async fn get_mca_status(
     state: &routes::SessionState,
     key_store: &domain::MerchantKeyStore,
@@ -4396,7 +4845,7 @@ pub async fn update_last_used_at(
 pub async fn get_bank_from_hs_locker(
     state: &routes::SessionState,
     key_store: &domain::MerchantKeyStore,
-    temp_token: &str,
+    temp_token: Option<&String>,
     customer_id: &id_type::CustomerId,
     merchant_id: &id_type::MerchantId,
     token_ref: &str,
@@ -4419,16 +4868,18 @@ pub async fn get_bank_from_hs_locker(
         .change_context(errors::ApiErrorResponse::InternalServerError)?;
     match &pm_parsed {
         api::PayoutMethodData::Bank(bank) => {
-            vault::Vault::store_payout_method_data_in_locker(
-                state,
-                Some(temp_token.to_string()),
-                &pm_parsed,
-                Some(customer_id.to_owned()),
-                key_store,
-            )
-            .await
-            .change_context(errors::ApiErrorResponse::InternalServerError)
-            .attach_printable("Error storing payout method data in temporary locker")?;
+            if let Some(token) = temp_token {
+                vault::Vault::store_payout_method_data_in_locker(
+                    state,
+                    Some(token.clone()),
+                    &pm_parsed,
+                    Some(customer_id.to_owned()),
+                    key_store,
+                )
+                .await
+                .change_context(errors::ApiErrorResponse::InternalServerError)
+                .attach_printable("Error storing payout method data in temporary locker")?;
+            }
             Ok(bank.to_owned())
         }
         api::PayoutMethodData::Card(_) => Err(errors::ApiErrorResponse::InvalidRequestData {
diff --git a/crates/router/src/core/payment_methods/surcharge_decision_configs.rs b/crates/router/src/core/payment_methods/surcharge_decision_configs.rs
index 6a3ceeada1c..23f79848dee 100644
--- a/crates/router/src/core/payment_methods/surcharge_decision_configs.rs
+++ b/crates/router/src/core/payment_methods/surcharge_decision_configs.rs
@@ -3,7 +3,16 @@ use api_models::{
     payments, routing,
     surcharge_decision_configs::{self, SurchargeDecisionConfigs, SurchargeDecisionManagerRecord},
 };
+#[cfg(all(
+    any(feature = "v1", feature = "v2"),
+    not(feature = "payment_methods_v2")
+))]
 use common_utils::{ext_traits::StringExt, types as common_utils_types};
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+use common_utils::{
+    ext_traits::{OptionExt, StringExt},
+    types as common_utils_types,
+};
 use error_stack::{self, ResultExt};
 use euclid::{
     backend,
@@ -267,6 +276,11 @@ where
     }
     Ok(surcharge_metadata)
 }
+
+#[cfg(all(
+    any(feature = "v1", feature = "v2"),
+    not(feature = "payment_methods_v2")
+))]
 pub async fn perform_surcharge_decision_management_for_saved_cards(
     state: &SessionState,
     algorithm_ref: routing::RoutingAlgorithmRef,
@@ -303,10 +317,13 @@ pub async fn perform_surcharge_decision_management_for_saved_cards(
         .change_context(ConfigError::InputConstructionError)?;
 
     for customer_payment_method in customer_payment_method_list.iter_mut() {
+        let payment_token = customer_payment_method.payment_token.clone();
+
         backend_input.payment_method.payment_method = Some(customer_payment_method.payment_method);
         backend_input.payment_method.payment_method_type =
             customer_payment_method.payment_method_type;
-        backend_input.payment_method.card_network = customer_payment_method
+
+        let card_network = customer_payment_method
             .card
             .as_ref()
             .and_then(|card| card.scheme.as_ref())
@@ -317,13 +334,98 @@ pub async fn perform_surcharge_decision_management_for_saved_cards(
                     .change_context(ConfigError::DslExecutionError)
             })
             .transpose()?;
+
+        backend_input.payment_method.card_network = card_network;
+
+        let surcharge_details = surcharge_source
+            .generate_surcharge_details_and_populate_surcharge_metadata(
+                &backend_input,
+                payment_attempt,
+                (
+                    &mut surcharge_metadata,
+                    types::SurchargeKey::Token(payment_token),
+                ),
+            )?;
+        customer_payment_method.surcharge_details = surcharge_details
+            .map(|surcharge_details| {
+                SurchargeDetailsResponse::foreign_try_from((&surcharge_details, payment_attempt))
+                    .change_context(ConfigError::DslParsingError)
+            })
+            .transpose()?;
+    }
+    Ok(surcharge_metadata)
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+pub async fn perform_surcharge_decision_management_for_saved_cards(
+    state: &SessionState,
+    algorithm_ref: routing::RoutingAlgorithmRef,
+    payment_attempt: &storage::PaymentAttempt,
+    payment_intent: &storage::PaymentIntent,
+    customer_payment_method_list: &mut [api_models::payment_methods::CustomerPaymentMethod],
+) -> ConditionalConfigResult<types::SurchargeMetadata> {
+    let mut surcharge_metadata = types::SurchargeMetadata::new(payment_attempt.attempt_id.clone());
+    let surcharge_source = match (
+        payment_attempt.get_surcharge_details(),
+        algorithm_ref.surcharge_config_algo_id,
+    ) {
+        (Some(request_surcharge_details), _) => {
+            SurchargeSource::Predetermined(request_surcharge_details)
+        }
+        (None, Some(algorithm_id)) => {
+            let cached_algo = ensure_algorithm_cached(
+                &*state.store,
+                &payment_attempt.merchant_id,
+                algorithm_id.as_str(),
+            )
+            .await?;
+
+            SurchargeSource::Generate(cached_algo)
+        }
+        (None, None) => return Ok(surcharge_metadata),
+    };
+    let surcharge_source_log_message = match &surcharge_source {
+        SurchargeSource::Generate(_) => "Surcharge was calculated through surcharge rules",
+        SurchargeSource::Predetermined(_) => "Surcharge was sent in payment create request",
+    };
+    logger::debug!(customer_saved_card_list_surcharge_source = surcharge_source_log_message);
+    let mut backend_input = make_dsl_input_for_surcharge(payment_attempt, payment_intent, None)
+        .change_context(ConfigError::InputConstructionError)?;
+
+    for customer_payment_method in customer_payment_method_list.iter_mut() {
+        let payment_token = customer_payment_method
+            .payment_token
+            .clone()
+            .get_required_value("payment_token")
+            .change_context(ConfigError::InputConstructionError)?;
+
+        backend_input.payment_method.payment_method = Some(customer_payment_method.payment_method);
+        backend_input.payment_method.payment_method_type =
+            customer_payment_method.payment_method_type;
+
+        let card_network = match &customer_payment_method.payment_method_data {
+            Some(api_models::payment_methods::PaymentMethodListData::Card(card)) => card
+                .scheme
+                .as_ref()
+                .map(|scheme| {
+                    scheme
+                        .clone()
+                        .parse_enum("CardNetwork")
+                        .change_context(ConfigError::DslExecutionError)
+                })
+                .transpose()?,
+            _ => None,
+        };
+
+        backend_input.payment_method.card_network = card_network;
+
         let surcharge_details = surcharge_source
             .generate_surcharge_details_and_populate_surcharge_metadata(
                 &backend_input,
                 payment_attempt,
                 (
                     &mut surcharge_metadata,
-                    types::SurchargeKey::Token(customer_payment_method.payment_token.clone()),
+                    types::SurchargeKey::Token(payment_token),
                 ),
             )?;
         customer_payment_method.surcharge_details = surcharge_details
diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs
index 708fcbbf406..2286ee8265c 100644
--- a/crates/router/src/routes/app.rs
+++ b/crates/router/src/routes/app.rs
@@ -30,7 +30,7 @@ use super::blocklist;
 use super::currency;
 #[cfg(feature = "dummy_connector")]
 use super::dummy_connector::*;
-#[cfg(all(any(feature = "olap", feature = "oltp"), not(feature = "customer_v2")))]
+#[cfg(any(feature = "olap", feature = "oltp"))]
 use super::payment_methods::*;
 #[cfg(feature = "payouts")]
 use super::payout_link::*;
@@ -501,7 +501,30 @@ impl DummyConnector {
 
 pub struct Payments;
 
-#[cfg(any(feature = "olap", feature = "oltp"))]
+#[cfg(all(
+    any(feature = "olap", feature = "oltp"),
+    feature = "v2",
+    feature = "payment_methods_v2",
+    feature = "payment_v2"
+))]
+impl Payments {
+    pub fn server(state: AppState) -> Scope {
+        let mut route = web::scope("/v2/payments").app_data(web::Data::new(state));
+        route = route.service(
+            web::resource("/{payment_id}/saved_payment_methods")
+                .route(web::get().to(list_customer_payment_method_for_payment)),
+        );
+
+        route
+    }
+}
+
+#[cfg(all(
+    any(feature = "olap", feature = "oltp"),
+    any(feature = "v2", feature = "v1"),
+    not(feature = "payment_methods_v2"),
+    not(feature = "payment_v2")
+))]
 impl Payments {
     pub fn server(state: AppState) -> Scope {
         let mut route = web::scope("/payments").app_data(web::Data::new(state));
@@ -590,7 +613,7 @@ impl Payments {
                 )
                 .service(
                     web::resource("/{payment_id}/extended_card_info").route(web::get().to(retrieve_extended_card_info)),
-                );
+                )
         }
         route
     }
@@ -844,6 +867,7 @@ pub struct Customers;
 #[cfg(all(
     feature = "v2",
     feature = "customer_v2",
+    feature = "payment_methods_v2",
     any(feature = "olap", feature = "oltp")
 ))]
 impl Customers {
@@ -853,6 +877,13 @@ impl Customers {
         {
             route = route.service(web::resource("").route(web::post().to(customers_create)))
         }
+        #[cfg(all(feature = "oltp", feature = "v2", feature = "payment_methods_v2"))]
+        {
+            route = route.service(
+                web::resource("/{customer_id}/saved_payment_methods")
+                    .route(web::get().to(list_customer_payment_method_api)),
+            );
+        }
         route
     }
 }
@@ -860,6 +891,7 @@ impl Customers {
 #[cfg(all(
     any(feature = "v1", feature = "v2"),
     not(feature = "customer_v2"),
+    not(feature = "payment_methods_v2"),
     any(feature = "olap", feature = "oltp")
 ))]
 impl Customers {
@@ -897,13 +929,12 @@ impl Customers {
                         .route(web::get().to(customers_retrieve))
                         .route(web::post().to(customers_update))
                         .route(web::delete().to(customers_delete)),
-                );
+                )
         }
 
         route
     }
 }
-
 pub struct Refunds;
 
 #[cfg(any(feature = "olap", feature = "oltp"))]
diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs
index f55f6c80e7a..6d7b3526b79 100644
--- a/crates/router/src/routes/payment_methods.rs
+++ b/crates/router/src/routes/payment_methods.rs
@@ -229,6 +229,11 @@ pub async fn list_payment_method_api(
     ))
     .await
 }
+
+#[cfg(all(
+    any(feature = "v2", feature = "v1"),
+    not(feature = "payment_methods_v2")
+))]
 /// List payment methods for a Customer
 ///
 /// To filter and list the applicable payment methods for a particular Customer ID
@@ -287,6 +292,134 @@ pub async fn list_customer_payment_method_api(
     ))
     .await
 }
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+/// List payment methods for a Customer v2
+///
+/// To filter and list the applicable payment methods for a particular Customer ID, is to be associated with a payment
+#[utoipa::path(
+    get,
+    path = "v2/payments/{payment_id}/saved_payment_methods",
+    params (
+        ("client-secret" = String, Path, description = "A secret known only to your application and the authorization server"),
+        ("accepted_country" = Vec<String>, Query, description = "The two-letter ISO currency code"),
+        ("accepted_currency" = Vec<Currency>, Path, description = "The three-letter ISO currency code"),
+        ("minimum_amount" = i64, Query, description = "The minimum amount accepted for processing by the particular payment method."),
+        ("maximum_amount" = i64, Query, description = "The maximum amount amount accepted for processing by the particular payment method."),
+        ("recurring_payment_enabled" = bool, Query, description = "Indicates whether the payment method is eligible for recurring payments"),
+        ("installment_payment_enabled" = bool, Query, description = "Indicates whether the payment method is eligible for installment payments"),
+    ),
+    responses(
+        (status = 200, description = "Payment Methods retrieved for customer tied to its respective client-secret passed in the param", body = CustomerPaymentMethodsListResponse),
+        (status = 400, description = "Invalid Data"),
+        (status = 404, description = "Payment Methods does not exist in records")
+    ),
+    tag = "Payment Methods",
+    operation_id = "List all Payment Methods for a Customer",
+    security(("publishable_key" = []))
+)]
+#[instrument(skip_all, fields(flow = ?Flow::CustomerPaymentMethodsList))]
+pub async fn list_customer_payment_method_for_payment(
+    state: web::Data<AppState>,
+    payment_id: web::Path<(String,)>,
+    req: HttpRequest,
+    query_payload: web::Query<payment_methods::PaymentMethodListRequest>,
+) -> HttpResponse {
+    let flow = Flow::CustomerPaymentMethodsList;
+    let payload = query_payload.into_inner();
+    let _payment_id = payment_id.into_inner().0.clone();
+
+    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),
+    };
+
+    Box::pin(api::server_wrap(
+        flow,
+        state,
+        &req,
+        payload,
+        |state, auth, req, _| {
+            cards::list_customer_payment_method_util(
+                state,
+                auth.merchant_account,
+                auth.key_store,
+                Some(req),
+                None,
+                true,
+            )
+        },
+        &*auth,
+        api_locking::LockAction::NotApplicable,
+    ))
+    .await
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+/// List payment methods for a Customer v2
+///
+/// To filter and list the applicable payment methods for a particular Customer ID, to be used in a non-payments context
+#[utoipa::path(
+    get,
+    path = "v2/customers/{customer_id}/saved_payment_methods",
+    params (
+        ("accepted_country" = Vec<String>, Query, description = "The two-letter ISO currency code"),
+        ("accepted_currency" = Vec<Currency>, Path, description = "The three-letter ISO currency code"),
+        ("minimum_amount" = i64, Query, description = "The minimum amount accepted for processing by the particular payment method."),
+        ("maximum_amount" = i64, Query, description = "The maximum amount amount accepted for processing by the particular payment method."),
+        ("recurring_payment_enabled" = bool, Query, description = "Indicates whether the payment method is eligible for recurring payments"),
+        ("installment_payment_enabled" = bool, Query, description = "Indicates whether the payment method is eligible for installment payments"),
+    ),
+    responses(
+        (status = 200, description = "Payment Methods retrieved", body = CustomerPaymentMethodsListResponse),
+        (status = 400, description = "Invalid Data"),
+        (status = 404, description = "Payment Methods does not exist in records")
+    ),
+    tag = "Payment Methods",
+    operation_id = "List all Payment Methods for a Customer",
+    security(("api_key" = []))
+)]
+#[instrument(skip_all, fields(flow = ?Flow::CustomerPaymentMethodsList))]
+pub async fn list_customer_payment_method_api(
+    state: web::Data<AppState>,
+    customer_id: web::Path<(id_type::CustomerId,)>,
+    req: HttpRequest,
+    query_payload: web::Query<payment_methods::PaymentMethodListRequest>,
+) -> HttpResponse {
+    let flow = Flow::CustomerPaymentMethodsList;
+    let payload = query_payload.into_inner();
+    let customer_id = customer_id.into_inner().0.clone();
+
+    let ephemeral_or_api_auth = match auth::is_ephemeral_auth(req.headers()) {
+        Ok(auth) => auth,
+        Err(err) => return api::log_and_return_error_response(err),
+    };
+
+    Box::pin(api::server_wrap(
+        flow,
+        state,
+        &req,
+        payload,
+        |state, auth, req, _| {
+            cards::list_customer_payment_method_util(
+                state,
+                auth.merchant_account,
+                auth.key_store,
+                Some(req),
+                Some(customer_id.clone()),
+                false,
+            )
+        },
+        &*ephemeral_or_api_auth,
+        api_locking::LockAction::NotApplicable,
+    ))
+    .await
+}
+
+#[cfg(all(
+    any(feature = "v2", feature = "v1"),
+    not(feature = "payment_methods_v2")
+))]
 /// List payment methods for a Customer
 ///
 /// To filter and list the applicable payment methods for a particular Customer ID
diff --git a/crates/router/src/types/api/payment_methods.rs b/crates/router/src/types/api/payment_methods.rs
index 77873503d6e..062a2fdf630 100644
--- a/crates/router/src/types/api/payment_methods.rs
+++ b/crates/router/src/types/api/payment_methods.rs
@@ -1,3 +1,19 @@
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+pub use api_models::payment_methods::{
+    CardDetail, CardDetailFromLocker, CardDetailsPaymentMethod, CustomerPaymentMethod,
+    CustomerPaymentMethodsListResponse, DefaultPaymentMethod, DeleteTokenizeByTokenRequest,
+    GetTokenizePayloadRequest, GetTokenizePayloadResponse, ListCountriesCurrenciesRequest,
+    PaymentMethodCollectLinkRenderRequest, PaymentMethodCollectLinkRequest, PaymentMethodCreate,
+    PaymentMethodCreateData, PaymentMethodDeleteResponse, PaymentMethodId, PaymentMethodList,
+    PaymentMethodListData, PaymentMethodListRequest, PaymentMethodListResponse,
+    PaymentMethodMigrate, PaymentMethodResponse, PaymentMethodUpdate, PaymentMethodsData,
+    TokenizePayloadEncrypted, TokenizePayloadRequest, TokenizedCardValue1, TokenizedCardValue2,
+    TokenizedWalletValue1, TokenizedWalletValue2,
+};
+#[cfg(all(
+    any(feature = "v2", feature = "v1"),
+    not(feature = "payment_methods_v2")
+))]
 pub use api_models::payment_methods::{
     CardDetail, CardDetailFromLocker, CardDetailsPaymentMethod, CustomerPaymentMethod,
     CustomerPaymentMethodsListResponse, DefaultPaymentMethod, DeleteTokenizeByTokenRequest,
diff --git a/crates/router/src/types/storage/payment_method.rs b/crates/router/src/types/storage/payment_method.rs
index 8a8f89f9017..05e3e566c07 100644
--- a/crates/router/src/types/storage/payment_method.rs
+++ b/crates/router/src/types/storage/payment_method.rs
@@ -81,7 +81,7 @@ impl PaymentTokenData {
 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 pub struct PaymentMethodListContext {
     pub card_details: Option<api::CardDetailFromLocker>,
-    pub hyperswitch_token_data: PaymentTokenData,
+    pub hyperswitch_token_data: Option<PaymentTokenData>,
     #[cfg(feature = "payouts")]
     pub bank_transfer_details: Option<api::BankPayout>,
 }

## 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 -->
Refactored customer's pml for v2

The customers Payment method list will be moved under two distinct endpoints:

    For Client (with payment tokens) - /payments/:id:/saved_payment_methods
    For Server (without payment token) - /customers/:cust_id:/saved_payment_methods

Payment tokens for the listed payment methods would be generated only in case of client calls in a payments context.

### Additional Changes

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

<!--
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->


## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->
- Save a card
- Create another payment 
```
curl --location --request POST 'http://localhost:8080/v2/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_cHN6hMdCFUUrNWjYSxnwhiuMsg83rXTX9jBmtHmCL6sq9a43PswY304MSt3a9blR' \
--data-raw '{
    "amount": 200,
    "currency": "USD",
    "confirm": false,
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "customer_id": "Some_cust2",
    "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",
    "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"
    }
}'
```
Response - 
```
{
    "payment_id": "pay_GNxoHRthb37nMZt6KLhv",
    "merchant_id": "sarthak1",
    "status": "requires_payment_method",
    "amount": 200,
    "net_amount": 200,
    "amount_capturable": 0,
    "amount_received": null,
    "connector": null,
    "client_secret": "pay_GNxoHRthb37nMZt6KLhv_secret_G97qG0GJ85JeGks8NmnA",
    "created": "2024-07-02T09:56:52.153Z",
    "currency": "USD",
    "customer_id": "Some_cust2",
    "customer": {
        "id": "Some_cust2",
        "name": "John Doe",
        "email": "guest@example.com",
        "phone": "999999999",
        "phone_country_code": "+1"
    },
    "description": "Its my first payment request",
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": null,
    "off_session": null,
    "capture_on": null,
    "capture_method": "automatic",
    "payment_method": null,
    "payment_method_data": null,
    "payment_token": null,
    "shipping": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "8056594430",
            "country_code": "+91"
        },
        "email": null
    },
    "billing": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "john",
            "last_name": "Doe"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": null
    },
    "order_details": null,
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "return_url": "https://google.com/",
    "authentication_type": "no_three_ds",
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "next_action": null,
    "cancellation_reason": null,
    "error_code": null,
    "error_message": null,
    "unified_code": null,
    "unified_message": null,
    "payment_experience": null,
    "payment_method_type": null,
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": {
        "customer_id": "Some_cust2",
        "created_at": 1719914212,
        "expires": 1719917812,
        "secret": "epk_7299286e8535424b957d32f6f5e44e99"
    },
    "manual_retry_allowed": null,
    "connector_transaction_id": null,
    "frm_message": null,
    "metadata": {
        "udf1": "value1",
        "login_date": "2019-09-10T10:11:12Z",
        "new_customer": "true"
    },
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": null,
    "payment_link": null,
    "profile_id": "pro_1HgRzJgJf21X3s7FPrhP",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": null,
    "incremental_authorization_allowed": null,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2024-07-02T10:11:52.153Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2024-07-02T09:56:52.226Z",
    "charges": null,
    "frm_metadata": null
}
```
- List saved payment method for v2 
```
curl --location --request GET 'http://localhost:8080/v2/payments/pay_GNxoHRthb37nMZt6KLhv/saved_payment_methods?client_secret=pay_GNxoHRthb37nMZt6KLhv_secret_G97qG0GJ85JeGks8NmnA' \
--header 'Accept: application/json' \
--header 'api-key: pk_dev_16e38a16176e45298a70da6c56c5028d'
```
Response - 
```
{
    "customer_payment_methods": [
        {
            "payment_token": "token_SBCX3r5nb9QaHNGtSbgK",
            "payment_method_id": "pm_R8n4IKXyEpAGcKaEQl0g",
            "customer_id": "Some_cust2",
            "payment_method": "card",
            "payment_method_type": null,
            "payment_method_issuer": null,
            "payment_method_issuer_code": null,
            "recurring_enabled": false,
            "installment_payment_enabled": false,
            "payment_experience": [
                "redirect_to_url"
            ],
            "payment_method_data": {
                "Card": {
                    "scheme": null,
                    "issuer_country": null,
                    "last4_digits": "4242",
                    "expiry_month": "10",
                    "expiry_year": "2025",
                    "card_token": null,
                    "card_holder_name": "joseph Doe",
                    "card_fingerprint": null,
                    "nick_name": null,
                    "card_network": null,
                    "card_isin": "424242",
                    "card_issuer": null,
                    "card_type": null,
                    "saved_to_locker": true
                }
            },
            "bank": null,
            "metadata": null,
            "created": "2024-06-26T08:26:47.674Z",
            "surcharge_details": null,
            "requires_cvv": true,
            "last_used_at": "2024-06-26T10:39:30.552Z",
            "default_payment_method_set": false,
            "billing": {
                "address": {
                    "city": "San Fransico",
                    "country": "US",
                    "line1": "1467",
                    "line2": "Harrison Street",
                    "line3": "Harrison Street",
                    "zip": "94122",
                    "state": "California",
                    "first_name": "john",
                    "last_name": "Doe"
                },
                "phone": {
                    "number": "8056594427",
                    "country_code": "+91"
                },
                "email": null
            }
        },
        {
            "payment_token": "token_nvHY8mkVcPYA94KQefdx",
            "payment_method_id": "pm_xFF1OOhEMqVQhzEv3b06",
            "customer_id": "Some_cust2",
            "payment_method": "card",
            "payment_method_type": null,
            "payment_method_issuer": null,
            "payment_method_issuer_code": null,
            "recurring_enabled": false,
            "installment_payment_enabled": false,
            "payment_experience": [
                "redirect_to_url"
            ],
            "payment_method_data": {
                "Card": {
                    "scheme": null,
                    "issuer_country": null,
                    "last4_digits": "4242",
                    "expiry_month": "10",
                    "expiry_year": "2025",
                    "card_token": null,
                    "card_holder_name": "joseph Doe",
                    "card_fingerprint": null,
                    "nick_name": null,
                    "card_network": null,
                    "card_isin": "424242",
                    "card_issuer": null,
                    "card_type": null,
                    "saved_to_locker": true
                }
            },
            "bank": null,
            "metadata": null,
            "created": "2024-06-13T11:55:47.828Z",
            "surcharge_details": null,
            "requires_cvv": true,
            "last_used_at": "2024-06-13T11:55:47.828Z",
            "default_payment_method_set": true,
            "billing": {
                "address": {
                    "city": "San Fransico",
                    "country": "US",
                    "line1": "1467",
                    "line2": "Harrison Street",
                    "line3": "Harrison Street",
                    "zip": "94122",
                    "state": "California",
                    "first_name": "john",
                    "last_name": "Doe"
                },
                "phone": {
                    "number": "8056594427",
                    "country_code": "+91"
                },
                "email": null
            }
        }
    ],
    "is_guest_customer": false
}
```
- Use Merchant API for saved payment methods - 
```
curl --location --request GET 'http://localhost:8080/v2/customers/Some_cust2/saved_payment_methods' \
--header 'Accept: application/json' \
--header 'api-key: dev_cHN6hMdCFUUrNWjYSxnwhiuMsg83rXTX9jBmtHmCL6sq9a43PswY304MSt3a9blR'
```
Response - 
```
{
    "customer_payment_methods": [
        {
            "payment_token": null,
            "payment_method_id": "pm_R8n4IKXyEpAGcKaEQl0g",
            "customer_id": "Some_cust2",
            "payment_method": "card",
            "payment_method_type": null,
            "payment_method_issuer": null,
            "payment_method_issuer_code": null,
            "recurring_enabled": false,
            "installment_payment_enabled": false,
            "payment_experience": [
                "redirect_to_url"
            ],
            "payment_method_data": {
                "Card": {
                    "scheme": null,
                    "issuer_country": null,
                    "last4_digits": "4242",
                    "expiry_month": "10",
                    "expiry_year": "2025",
                    "card_token": null,
                    "card_holder_name": "joseph Doe",
                    "card_fingerprint": null,
                    "nick_name": null,
                    "card_network": null,
                    "card_isin": "424242",
                    "card_issuer": null,
                    "card_type": null,
                    "saved_to_locker": true
                }
            },
            "bank": null,
            "metadata": null,
            "created": "2024-06-26T08:26:47.674Z",
            "surcharge_details": null,
            "requires_cvv": false,
            "last_used_at": "2024-06-26T10:39:30.552Z",
            "default_payment_method_set": false,
            "billing": {
                "address": {
                    "city": "San Fransico",
                    "country": "US",
                    "line1": "1467",
                    "line2": "Harrison Street",
                    "line3": "Harrison Street",
                    "zip": "94122",
                    "state": "California",
                    "first_name": "john",
                    "last_name": "Doe"
                },
                "phone": {
                    "number": "8056594427",
                    "country_code": "+91"
                },
                "email": null
            }
        },
        {
            "payment_token": null,
            "payment_method_id": "pm_xFF1OOhEMqVQhzEv3b06",
            "customer_id": "Some_cust2",
            "payment_method": "card",
            "payment_method_type": null,
            "payment_method_issuer": null,
            "payment_method_issuer_code": null,
            "recurring_enabled": false,
            "installment_payment_enabled": false,
            "payment_experience": [
                "redirect_to_url"
            ],
            "payment_method_data": {
                "Card": {
                    "scheme": null,
                    "issuer_country": null,
                    "last4_digits": "4242",
                    "expiry_month": "10",
                    "expiry_year": "2025",
                    "card_token": null,
                    "card_holder_name": "joseph Doe",
                    "card_fingerprint": null,
                    "nick_name": null,
                    "card_network": null,
                    "card_isin": "424242",
                    "card_issuer": null,
                    "card_type": null,
                    "saved_to_locker": true
                }
            },
            "bank": null,
            "metadata": null,
            "created": "2024-06-13T11:55:47.828Z",
            "surcharge_details": null,
            "requires_cvv": false,
            "last_used_at": "2024-06-13T11:55:47.828Z",
            "default_payment_method_set": true,
            "billing": {
                "address": {
                    "city": "San Fransico",
                    "country": "US",
                    "line1": "1467",
                    "line2": "Harrison Street",
                    "line3": "Harrison Street",
                    "zip": "94122",
                    "state": "California",
                    "first_name": "john",
                    "last_name": "Doe"
                },
                "phone": {
                    "number": "8056594427",
                    "country_code": "+91"
                },
                "email": null
            }
        }
    ],
    "is_guest_customer": null
}
```

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