Bug: Refactor `/payment_methods` to support a unified interface for vaulting use-cases



### Feature Description
We want a unified interface to `/payment_methods` create APIs for vaulting a payment method. Request/Response types and their behavior should be same across the vaulting APIs.
Involved APIs would be PM create and PM save. 

### Possible Implementation
Can involve -  
- refactoring of Req/Res (and other relevant types) for vaulting APIs
- refactoring of core logic for vaulting APIs
diff --git a/crates/api_models/Cargo.toml b/crates/api_models/Cargo.toml
index 00c12b9255b..c16b1d9a418 100644
--- a/crates/api_models/Cargo.toml
+++ b/crates/api_models/Cargo.toml
@@ -20,7 +20,7 @@ v1 = ["common_utils/v1"]
 v2 = ["common_utils/v2", "customer_v2"]
 customer_v2 = ["common_utils/customer_v2"]
 payment_v2 = []
-payment_methods_v2 = []
+payment_methods_v2 = ["common_utils/payment_methods_v2"]
 
 [dependencies]
 actix-web = { version = "4.5.1", optional = true }
diff --git a/crates/api_models/src/events.rs b/crates/api_models/src/events.rs
index 58727a46cf3..13966c464d5 100644
--- a/crates/api_models/src/events.rs
+++ b/crates/api_models/src/events.rs
@@ -152,3 +152,21 @@ impl<T> ApiEventMetric for MetricsResponse<T> {
         Some(ApiEventsType::Miscellaneous)
     }
 }
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+impl ApiEventMetric for PaymentMethodIntentConfirmInternal {
+    fn get_api_event_type(&self) -> Option<ApiEventsType> {
+        Some(ApiEventsType::PaymentMethod {
+            payment_method_id: self.id.clone(),
+            payment_method: Some(self.payment_method),
+            payment_method_type: Some(self.payment_method_type),
+        })
+    }
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+impl ApiEventMetric for PaymentMethodIntentCreate {
+    fn get_api_event_type(&self) -> Option<ApiEventsType> {
+        Some(ApiEventsType::PaymentMethodCreate)
+    }
+}
diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs
index c6cf6380150..6c93b0bb5bd 100644
--- a/crates/api_models/src/payment_methods.rs
+++ b/crates/api_models/src/payment_methods.rs
@@ -160,8 +160,68 @@ pub struct PaymentMethodIntentConfirm {
 
     /// Payment method data to be passed
     pub payment_method_data: PaymentMethodCreateData,
+
+    /// 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 = PaymentMethodType,example = "credit")]
+    pub payment_method_type: api_enums::PaymentMethodType,
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+impl PaymentMethodIntentConfirm {
+    pub fn validate_payment_method_data_against_payment_method(
+        payment_method: api_enums::PaymentMethod,
+        payment_method_data: PaymentMethodCreateData,
+    ) -> bool {
+        match payment_method {
+            api_enums::PaymentMethod::Card => {
+                matches!(payment_method_data, PaymentMethodCreateData::Card(_))
+            }
+            _ => false,
+        }
+    }
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
+#[serde(deny_unknown_fields)]
+pub struct PaymentMethodIntentConfirmInternal {
+    #[schema(value_type = Option<String>, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
+    pub id: String,
+    /// 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 = PaymentMethodType,example = "credit")]
+    pub payment_method_type: api_enums::PaymentMethodType,
+
+    /// For SDK based calls, client_secret would be required
+    pub client_secret: String,
+
+    /// The unique identifier of the customer.
+    #[schema(value_type = Option<String>, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
+    pub customer_id: Option<id_type::CustomerId>,
+
+    /// Payment method data to be passed
+    pub payment_method_data: PaymentMethodCreateData,
 }
 
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+impl From<PaymentMethodIntentConfirmInternal> for PaymentMethodIntentConfirm {
+    fn from(item: PaymentMethodIntentConfirmInternal) -> Self {
+        Self {
+            client_secret: item.client_secret,
+            payment_method: item.payment_method,
+            payment_method_type: item.payment_method_type,
+            customer_id: item.customer_id,
+            payment_method_data: item.payment_method_data.clone(),
+        }
+    }
+}
 #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
 /// This struct is only used by and internal api to migrate payment method
 pub struct PaymentMethodMigrate {
@@ -276,11 +336,16 @@ impl PaymentMethodCreate {
 
 #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
 impl PaymentMethodCreate {
-    pub fn get_payment_method_create_from_payment_method_migrate(
-        _card_number: CardNumber,
-        _payment_method_migrate: &PaymentMethodMigrate,
-    ) -> Self {
-        todo!()
+    pub fn validate_payment_method_data_against_payment_method(
+        payment_method: api_enums::PaymentMethod,
+        payment_method_data: PaymentMethodCreateData,
+    ) -> bool {
+        match payment_method {
+            api_enums::PaymentMethod::Card => {
+                matches!(payment_method_data, PaymentMethodCreateData::Card(_))
+            }
+            _ => false,
+        }
     }
 }
 
@@ -332,12 +397,6 @@ pub enum PaymentMethodUpdateData {
 #[serde(rename = "payment_method_data")]
 pub enum PaymentMethodCreateData {
     Card(CardDetail),
-    #[cfg(feature = "payouts")]
-    #[schema(value_type = Bank)]
-    BankTransfer(payouts::Bank),
-    #[cfg(feature = "payouts")]
-    #[schema(value_type = Wallet)]
-    Wallet(payouts::Wallet),
 }
 
 #[cfg(all(
@@ -577,9 +636,6 @@ impl CardDetailUpdate {
 #[serde(rename = "payment_method_data")]
 pub enum PaymentMethodResponseData {
     Card(CardDetailFromLocker),
-    #[cfg(feature = "payouts")]
-    #[schema(value_type = Bank)]
-    Bank(payouts::Bank),
 }
 
 #[cfg(all(
@@ -934,6 +990,25 @@ impl From<CardDetailsPaymentMethod> for CardDetailFromLocker {
     }
 }
 
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+impl From<CardDetail> for CardDetailsPaymentMethod {
+    fn from(item: CardDetail) -> Self {
+        Self {
+            issuer_country: item.card_issuing_country.map(|c| c.to_string()),
+            last4_digits: Some(item.card_number.get_last4()),
+            expiry_month: Some(item.card_exp_month),
+            expiry_year: Some(item.card_exp_year),
+            card_holder_name: item.card_holder_name,
+            nick_name: item.nick_name,
+            card_isin: None,
+            card_issuer: item.card_issuer,
+            card_network: item.card_network,
+            card_type: item.card_type.map(|card| card.to_string()),
+            saved_to_locker: true,
+        }
+    }
+}
+
 #[cfg(all(
     any(feature = "v1", feature = "v2"),
     not(feature = "payment_methods_v2")
diff --git a/crates/common_utils/Cargo.toml b/crates/common_utils/Cargo.toml
index c6e29801b2f..fdc9670045e 100644
--- a/crates/common_utils/Cargo.toml
+++ b/crates/common_utils/Cargo.toml
@@ -21,6 +21,7 @@ payouts = ["common_enums/payouts"]
 v1 = []
 v2 = []
 customer_v2 = []
+payment_methods_v2 = []
 
 [dependencies]
 async-trait = { version = "0.1.79", optional = true }
diff --git a/crates/common_utils/src/events.rs b/crates/common_utils/src/events.rs
index 1e752a0f9b9..16d0fd9b30a 100644
--- a/crates/common_utils/src/events.rs
+++ b/crates/common_utils/src/events.rs
@@ -27,6 +27,8 @@ pub enum ApiEventsType {
         payment_method: Option<PaymentMethod>,
         payment_method_type: Option<PaymentMethodType>,
     },
+    #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+    PaymentMethodCreate,
     #[cfg(all(feature = "v2", feature = "customer_v2"))]
     Customer {
         id: String,
diff --git a/crates/common_utils/src/id_type.rs b/crates/common_utils/src/id_type.rs
index 14738811835..20dc423310a 100644
--- a/crates/common_utils/src/id_type.rs
+++ b/crates/common_utils/src/id_type.rs
@@ -12,6 +12,8 @@ mod profile;
 mod routing;
 
 mod global_id;
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+mod payment_methods;
 
 pub use customer::CustomerId;
 use diesel::{
@@ -25,6 +27,8 @@ pub use merchant::MerchantId;
 pub use merchant_connector_account::MerchantConnectorAccountId;
 pub use organization::OrganizationId;
 pub use payment::PaymentId;
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+pub use payment_methods::GlobalPaymentMethodId;
 pub use profile::ProfileId;
 pub use routing::RoutingId;
 use serde::{Deserialize, Serialize};
diff --git a/crates/common_utils/src/id_type/global_id.rs b/crates/common_utils/src/id_type/global_id.rs
index f04bfc3f92b..8efbc3636f9 100644
--- a/crates/common_utils/src/id_type/global_id.rs
+++ b/crates/common_utils/src/id_type/global_id.rs
@@ -21,6 +21,7 @@ pub(crate) struct GlobalId(LengthId<MAX_GLOBAL_ID_LENGTH, MIN_GLOBAL_ID_LENGTH>)
 pub(crate) enum GlobalEntity {
     Customer,
     Payment,
+    PaymentMethod,
 }
 
 impl GlobalEntity {
@@ -28,6 +29,7 @@ impl GlobalEntity {
         match self {
             Self::Customer => "cus",
             Self::Payment => "pay",
+            Self::PaymentMethod => "pm",
         }
     }
 }
diff --git a/crates/common_utils/src/id_type/payment_methods.rs b/crates/common_utils/src/id_type/payment_methods.rs
new file mode 100644
index 00000000000..f5cc2150913
--- /dev/null
+++ b/crates/common_utils/src/id_type/payment_methods.rs
@@ -0,0 +1,85 @@
+use diesel::{backend::Backend, deserialize::FromSql, serialize::ToSql, sql_types};
+use error_stack::ResultExt;
+
+use crate::{
+    errors,
+    errors::CustomResult,
+    id_type::global_id::{CellId, GlobalEntity, GlobalId, GlobalIdError},
+};
+
+#[derive(
+    Debug,
+    Clone,
+    Hash,
+    PartialEq,
+    Eq,
+    serde::Serialize,
+    serde::Deserialize,
+    diesel::expression::AsExpression,
+)]
+#[diesel(sql_type = diesel::sql_types::Text)]
+pub struct GlobalPaymentMethodId(GlobalId);
+
+#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
+pub enum GlobalPaymentMethodIdError {
+    #[error("Failed to construct GlobalPaymentMethodId")]
+    ConstructionError,
+}
+
+impl GlobalPaymentMethodId {
+    fn get_global_id(&self) -> &GlobalId {
+        &self.0
+    }
+    /// Create a new GlobalPaymentMethodId from celll id information
+    pub fn generate(cell_id: &str) -> error_stack::Result<Self, errors::ValidationError> {
+        let cell_id = CellId::from_string(cell_id.to_string())?;
+        let global_id = GlobalId::generate(cell_id, GlobalEntity::PaymentMethod);
+        Ok(Self(global_id))
+    }
+
+    pub fn get_string_repr(&self) -> String {
+        todo!()
+    }
+
+    pub fn generate_from_string(value: String) -> CustomResult<Self, GlobalPaymentMethodIdError> {
+        let id = GlobalId::from_string(value)
+            .change_context(GlobalPaymentMethodIdError::ConstructionError)?;
+        Ok(Self(id))
+    }
+}
+
+impl<DB> diesel::Queryable<diesel::sql_types::Text, DB> for GlobalPaymentMethodId
+where
+    DB: diesel::backend::Backend,
+    Self: diesel::deserialize::FromSql<diesel::sql_types::Text, DB>,
+{
+    type Row = Self;
+    fn build(row: Self::Row) -> diesel::deserialize::Result<Self> {
+        Ok(row)
+    }
+}
+
+impl<DB> ToSql<sql_types::Text, DB> for GlobalPaymentMethodId
+where
+    DB: Backend,
+    GlobalId: ToSql<sql_types::Text, DB>,
+{
+    fn to_sql<'b>(
+        &'b self,
+        out: &mut diesel::serialize::Output<'b, '_, DB>,
+    ) -> diesel::serialize::Result {
+        let id = self.get_global_id();
+        id.to_sql(out)
+    }
+}
+
+impl<DB> FromSql<sql_types::Text, DB> for GlobalPaymentMethodId
+where
+    DB: Backend,
+    GlobalId: FromSql<sql_types::Text, DB>,
+{
+    fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result<Self> {
+        let global_id = GlobalId::from_sql(value)?;
+        Ok(Self(global_id))
+    }
+}
diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs
index ca919eb15da..3ad58e62dcf 100644
--- a/crates/diesel_models/src/payment_method.rs
+++ b/crates/diesel_models/src/payment_method.rs
@@ -88,7 +88,7 @@ pub struct PaymentMethod {
     pub payment_method_billing_address: Option<Encryption>,
     pub updated_by: Option<String>,
     pub locker_fingerprint_id: Option<String>,
-    pub id: String,
+    pub id: common_utils::id_type::GlobalPaymentMethodId,
     pub version: common_enums::ApiVersion,
     pub network_token_requestor_reference_id: Option<String>,
     pub network_token_locker_id: Option<String>,
@@ -105,7 +105,7 @@ impl PaymentMethod {
     }
 
     #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
-    pub fn get_id(&self) -> &String {
+    pub fn get_id(&self) -> &common_utils::id_type::GlobalPaymentMethodId {
         &self.id
     }
 }
@@ -179,7 +179,7 @@ pub struct PaymentMethodNew {
     pub payment_method_billing_address: Option<Encryption>,
     pub updated_by: Option<String>,
     pub locker_fingerprint_id: Option<String>,
-    pub id: String,
+    pub id: common_utils::id_type::GlobalPaymentMethodId,
     pub version: common_enums::ApiVersion,
     pub network_token_requestor_reference_id: Option<String>,
     pub network_token_locker_id: Option<String>,
@@ -200,7 +200,7 @@ impl PaymentMethodNew {
     }
 
     #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
-    pub fn get_id(&self) -> &String {
+    pub fn get_id(&self) -> &common_utils::id_type::GlobalPaymentMethodId {
         &self.id
     }
 }
diff --git a/crates/diesel_models/src/query/payment_method.rs b/crates/diesel_models/src/query/payment_method.rs
index 37c41c19dd9..321c7a6829c 100644
--- a/crates/diesel_models/src/query/payment_method.rs
+++ b/crates/diesel_models/src/query/payment_method.rs
@@ -211,7 +211,10 @@ impl PaymentMethod {
 
 #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
 impl PaymentMethod {
-    pub async fn find_by_id(conn: &PgPooledConn, id: &str) -> StorageResult<Self> {
+    pub async fn find_by_id(
+        conn: &PgPooledConn,
+        id: &common_utils::id_type::GlobalPaymentMethodId,
+    ) -> StorageResult<Self> {
         generics::generic_find_one::<<Self as HasTable>::Table, _, _>(conn, pm_id.eq(id.to_owned()))
             .await
     }
diff --git a/crates/hyperswitch_domain_models/src/payment_methods.rs b/crates/hyperswitch_domain_models/src/payment_methods.rs
index e7cbae9e88c..af3c657c34a 100644
--- a/crates/hyperswitch_domain_models/src/payment_methods.rs
+++ b/crates/hyperswitch_domain_models/src/payment_methods.rs
@@ -77,7 +77,7 @@ pub struct PaymentMethod {
     pub payment_method_billing_address: OptionalEncryptableValue,
     pub updated_by: Option<String>,
     pub locker_fingerprint_id: Option<String>,
-    pub id: String,
+    pub id: common_utils::id_type::GlobalPaymentMethodId,
     pub version: common_enums::ApiVersion,
     pub network_token_requestor_reference_id: Option<String>,
     pub network_token_locker_id: Option<String>,
@@ -94,7 +94,7 @@ impl PaymentMethod {
     }
 
     #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
-    pub fn get_id(&self) -> &String {
+    pub fn get_id(&self) -> &common_utils::id_type::GlobalPaymentMethodId {
         &self.id
     }
 }
diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml
index 0c6e06cc42d..bae6243d2e6 100644
--- a/crates/router/Cargo.toml
+++ b/crates/router/Cargo.toml
@@ -37,7 +37,7 @@ v2 = ["customer_v2", "payment_methods_v2", "payment_v2", "common_default", "api_
 v1 = ["common_default", "api_models/v1", "diesel_models/v1", "hyperswitch_domain_models/v1", "storage_impl/v1", "hyperswitch_interfaces/v1", "kgraph_utils/v1"]
 customer_v2 = ["api_models/customer_v2", "diesel_models/customer_v2", "hyperswitch_domain_models/customer_v2", "storage_impl/customer_v2"]
 payment_v2 = ["api_models/payment_v2", "diesel_models/payment_v2", "hyperswitch_domain_models/payment_v2", "storage_impl/payment_v2"]
-payment_methods_v2 = ["api_models/payment_methods_v2", "diesel_models/payment_methods_v2", "hyperswitch_domain_models/payment_methods_v2", "storage_impl/payment_methods_v2"]
+payment_methods_v2 = ["api_models/payment_methods_v2", "diesel_models/payment_methods_v2", "hyperswitch_domain_models/payment_methods_v2", "storage_impl/payment_methods_v2", "common_utils/payment_methods_v2"]
 
 # Partial Auth
 # The feature reduces the overhead of the router authenticating the merchant for every request, and trusts on `x-merchant-id` header to be present in the request.
diff --git a/crates/router/src/consts.rs b/crates/router/src/consts.rs
index 3d4a29e0f30..1f4fd00bbc5 100644
--- a/crates/router/src/consts.rs
+++ b/crates/router/src/consts.rs
@@ -135,3 +135,15 @@ pub const DEFAULT_UNIFIED_ERROR_MESSAGE: &str = "Something went wrong";
 
 // Recon's feature tag
 pub const RECON_FEATURE_TAG: &str = "RECONCILIATION AND SETTLEMENT";
+
+/// Vault Add request url
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+pub const ADD_VAULT_REQUEST_URL: &str = "/vault/add";
+
+/// Vault Get Fingerprint request url
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+pub const VAULT_FINGERPRINT_REQUEST_URL: &str = "/fingerprint";
+
+/// Vault Header content type
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+pub const VAULT_HEADER_CONTENT_TYPE: &str = "application/json";
diff --git a/crates/router/src/core/errors.rs b/crates/router/src/core/errors.rs
index d8a28ebdf2e..c56bfaca913 100644
--- a/crates/router/src/core/errors.rs
+++ b/crates/router/src/core/errors.rs
@@ -148,6 +148,12 @@ pub enum VaultError {
     SavePaymentMethodFailed,
     #[error("Failed to generate fingerprint")]
     GenerateFingerprintFailed,
+    #[error("Failed to encrypt vault request")]
+    RequestEncryptionFailed,
+    #[error("Failed to decrypt vault response")]
+    ResponseDecryptionFailed,
+    #[error("Failed to call vault")]
+    VaultAPIError,
     #[error("Failed while calling locker API")]
     ApiError,
 }
diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs
index 72fbc3d3d4e..fa7ddfce997 100644
--- a/crates/router/src/core/payment_methods.rs
+++ b/crates/router/src/core/payment_methods.rs
@@ -21,9 +21,16 @@ use api_models::payment_methods;
 pub use api_models::{enums::PayoutConnectors, payouts as payout_types};
 #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))]
 use common_utils::ext_traits::Encode;
-#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
-use common_utils::ext_traits::OptionExt;
 use common_utils::{consts::DEFAULT_LOCALE, id_type::CustomerId};
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+use common_utils::{
+    crypto::{self, Encryptable},
+    ext_traits::{AsyncExt, Encode, ValueExt},
+    fp_utils::when,
+    generate_id, id_type,
+    request::RequestContent,
+    types as util_types,
+};
 use diesel_models::{
     enums, GenericLinkNew, PaymentMethodCollectLink, PaymentMethodCollectLinkData,
 };
@@ -31,7 +38,11 @@ use error_stack::{report, ResultExt};
 #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))]
 use hyperswitch_domain_models::api::{GenericLinks, GenericLinksData};
 use hyperswitch_domain_models::payments::{payment_attempt::PaymentAttempt, PaymentIntent};
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+use masking::ExposeInterface;
 use masking::PeekInterface;
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+use masking::Secret;
 use router_env::{instrument, tracing};
 use time::Duration;
 
@@ -39,11 +50,25 @@ use super::{
     errors::{RouterResponse, StorageErrorExt},
     pm_auth,
 };
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+use crate::{
+    configs::settings,
+    core::{payment_methods::transformers as pm_transforms, utils as core_utils},
+    headers, logger,
+    routes::payment_methods as pm_routes,
+    services::encryption,
+    types::{
+        api::{self, payment_methods::PaymentMethodCreateExt},
+        payment_methods as pm_types,
+        storage::PaymentMethodListContext,
+    },
+    utils::ext_traits::OptionExt,
+};
 use crate::{
     consts,
     core::{
         errors::{self, RouterResult},
-        payments::helpers,
+        payments::helpers as payment_helpers,
     },
     routes::{app::StorageInterface, SessionState},
     services,
@@ -67,7 +92,7 @@ pub async fn retrieve_payment_method(
 ) -> RouterResult<(Option<domain::PaymentMethodData>, Option<String>)> {
     match pm_data {
         pm_opt @ Some(pm @ domain::PaymentMethodData::Card(_)) => {
-            let payment_token = helpers::store_payment_method_data_in_vault(
+            let payment_token = payment_helpers::store_payment_method_data_in_vault(
                 state,
                 payment_attempt,
                 payment_intent,
@@ -81,7 +106,7 @@ pub async fn retrieve_payment_method(
             Ok((pm_opt.to_owned(), payment_token))
         }
         pm_opt @ Some(pm @ domain::PaymentMethodData::BankDebit(_)) => {
-            let payment_token = helpers::store_payment_method_data_in_vault(
+            let payment_token = payment_helpers::store_payment_method_data_in_vault(
                 state,
                 payment_attempt,
                 payment_intent,
@@ -104,7 +129,7 @@ pub async fn retrieve_payment_method(
         pm @ Some(domain::PaymentMethodData::GiftCard(_)) => Ok((pm.to_owned(), None)),
         pm @ Some(domain::PaymentMethodData::OpenBanking(_)) => Ok((pm.to_owned(), None)),
         pm_opt @ Some(pm @ domain::PaymentMethodData::BankTransfer(_)) => {
-            let payment_token = helpers::store_payment_method_data_in_vault(
+            let payment_token = payment_helpers::store_payment_method_data_in_vault(
                 state,
                 payment_attempt,
                 payment_intent,
@@ -118,7 +143,7 @@ pub async fn retrieve_payment_method(
             Ok((pm_opt.to_owned(), payment_token))
         }
         pm_opt @ Some(pm @ domain::PaymentMethodData::Wallet(_)) => {
-            let payment_token = helpers::store_payment_method_data_in_vault(
+            let payment_token = payment_helpers::store_payment_method_data_in_vault(
                 state,
                 payment_attempt,
                 payment_intent,
@@ -132,7 +157,7 @@ pub async fn retrieve_payment_method(
             Ok((pm_opt.to_owned(), payment_token))
         }
         pm_opt @ Some(pm @ domain::PaymentMethodData::BankRedirect(_)) => {
-            let payment_token = helpers::store_payment_method_data_in_vault(
+            let payment_token = payment_helpers::store_payment_method_data_in_vault(
                 state,
                 payment_attempt,
                 payment_intent,
@@ -414,6 +439,10 @@ fn generate_task_id_for_payment_method_status_update_workflow(
     format!("{runner}_{task}_{key_id}")
 }
 
+#[cfg(all(
+    any(feature = "v1", feature = "v2"),
+    not(feature = "payment_methods_v2")
+))]
 pub async fn add_payment_method_status_update_task(
     db: &dyn StorageInterface,
     payment_method: &domain::PaymentMethod,
@@ -483,7 +512,10 @@ pub async fn retrieve_payment_method_with_token(
     todo!()
 }
 
-#[cfg(feature = "v1")]
+#[cfg(all(
+    any(feature = "v1", feature = "v2"),
+    not(feature = "payment_methods_v2")
+))]
 #[instrument(skip_all)]
 #[allow(clippy::too_many_arguments)]
 pub async fn retrieve_payment_method_with_token(
@@ -500,7 +532,7 @@ pub async fn retrieve_payment_method_with_token(
 ) -> RouterResult<storage::PaymentMethodDataWithId> {
     let token = match token_data {
         storage::PaymentTokenData::TemporaryGeneric(generic_token) => {
-            helpers::retrieve_payment_method_with_temporary_token(
+            payment_helpers::retrieve_payment_method_with_temporary_token(
                 state,
                 &generic_token.token,
                 payment_intent,
@@ -519,7 +551,7 @@ pub async fn retrieve_payment_method_with_token(
         }
 
         storage::PaymentTokenData::Temporary(generic_token) => {
-            helpers::retrieve_payment_method_with_temporary_token(
+            payment_helpers::retrieve_payment_method_with_temporary_token(
                 state,
                 &generic_token.token,
                 payment_intent,
@@ -538,7 +570,7 @@ pub async fn retrieve_payment_method_with_token(
         }
 
         storage::PaymentTokenData::Permanent(card_token) => {
-            helpers::retrieve_card_with_permanent_token(
+            payment_helpers::retrieve_card_with_permanent_token(
                 state,
                 card_token.locker_id.as_ref().unwrap_or(&card_token.token),
                 card_token
@@ -572,7 +604,7 @@ pub async fn retrieve_payment_method_with_token(
         }
 
         storage::PaymentTokenData::PermanentCard(card_token) => {
-            helpers::retrieve_card_with_permanent_token(
+            payment_helpers::retrieve_card_with_permanent_token(
                 state,
                 card_token.locker_id.as_ref().unwrap_or(&card_token.token),
                 card_token
@@ -793,3 +825,950 @@ pub(crate) async fn get_payment_method_create_request(
         .attach_printable("PaymentMethodData required Or Card is already saved")),
     }
 }
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[instrument(skip_all)]
+pub async fn create_payment_method(
+    state: &SessionState,
+    req: api::PaymentMethodCreate,
+    merchant_account: &domain::MerchantAccount,
+    key_store: &domain::MerchantKeyStore,
+) -> errors::RouterResponse<api::PaymentMethodResponse> {
+    req.validate()?;
+
+    let db = &*state.store;
+    let merchant_id = merchant_account.get_id();
+    let customer_id = req.customer_id.to_owned();
+
+    db.find_customer_by_merchant_reference_id_merchant_id(
+        &(state.into()),
+        &customer_id,
+        merchant_account.get_id(),
+        &key_store,
+        merchant_account.storage_scheme,
+    )
+    .await
+    .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?;
+
+    let payment_method_billing_address: Option<Encryptable<Secret<serde_json::Value>>> = req
+        .billing
+        .clone()
+        .async_map(|billing| cards::create_encrypted_data(state, key_store, billing))
+        .await
+        .transpose()
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Unable to encrypt Payment method billing address")?;
+
+    // create pm
+    let payment_method_id =
+        common_utils::id_type::GlobalPaymentMethodId::generate("random_cell_id")
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("Unable to generate GlobalPaymentMethodId")?;
+
+    let payment_method = create_payment_method_for_intent(
+        state,
+        req.metadata.clone(),
+        &customer_id,
+        payment_method_id,
+        merchant_id,
+        key_store,
+        merchant_account.storage_scheme,
+        payment_method_billing_address.map(Into::into),
+    )
+    .await
+    .attach_printable("Failed to add Payment method to DB")?;
+
+    let vaulting_result =
+        vault_payment_method(state, &req.payment_method_data, merchant_account, key_store).await;
+
+    let response = match vaulting_result {
+        Ok(resp) => {
+            let pm_update = create_pm_additional_data_update(
+                &req.payment_method_data,
+                state,
+                key_store,
+                Some(resp.vault_id),
+                Some(req.payment_method),
+                Some(req.payment_method_type),
+            )
+            .await
+            .attach_printable("Unable to create Payment method data")?;
+
+            let payment_method = db
+                .update_payment_method(
+                    &(state.into()),
+                    &key_store,
+                    payment_method,
+                    pm_update,
+                    merchant_account.storage_scheme,
+                )
+                .await
+                .change_context(errors::ApiErrorResponse::InternalServerError)
+                .attach_printable("Failed to update payment method in db")?;
+
+            let resp = pm_transforms::generate_payment_method_response(&payment_method)?;
+
+            Ok(resp)
+        }
+        Err(e) => {
+            let pm_update = storage::PaymentMethodUpdate::StatusUpdate {
+                status: Some(enums::PaymentMethodStatus::Inactive),
+            };
+
+            db.update_payment_method(
+                &(state.into()),
+                &key_store,
+                payment_method,
+                pm_update,
+                merchant_account.storage_scheme,
+            )
+            .await
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("Failed to update payment method in db")?;
+
+            Err(e)
+        }
+    }?;
+
+    Ok(services::ApplicationResponse::Json(response))
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[instrument(skip_all)]
+pub async fn payment_method_intent_create(
+    state: &SessionState,
+    req: api::PaymentMethodIntentCreate,
+    merchant_account: &domain::MerchantAccount,
+    key_store: &domain::MerchantKeyStore,
+) -> errors::RouterResponse<api::PaymentMethodResponse> {
+    let db = &*state.store;
+    let merchant_id = merchant_account.get_id();
+    let customer_id = req.customer_id.to_owned();
+
+    db.find_customer_by_merchant_reference_id_merchant_id(
+        &(state.into()),
+        &customer_id,
+        merchant_account.get_id(),
+        &key_store,
+        merchant_account.storage_scheme,
+    )
+    .await
+    .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?;
+
+    let payment_method_billing_address: Option<Encryptable<Secret<serde_json::Value>>> = req
+        .billing
+        .clone()
+        .async_map(|billing| cards::create_encrypted_data(state, key_store, billing))
+        .await
+        .transpose()
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Unable to encrypt Payment method billing address")?;
+
+    // create pm entry
+
+    let payment_method_id =
+        common_utils::id_type::GlobalPaymentMethodId::generate("random_cell_id")
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("Unable to generate GlobalPaymentMethodId")?;
+
+    let payment_method = create_payment_method_for_intent(
+        state,
+        req.metadata.clone(),
+        &customer_id,
+        payment_method_id,
+        merchant_id,
+        key_store,
+        merchant_account.storage_scheme,
+        payment_method_billing_address.map(Into::into),
+    )
+    .await
+    .attach_printable("Failed to add Payment method to DB")?;
+
+    let resp = pm_transforms::generate_payment_method_response(&payment_method)?;
+
+    Ok(services::ApplicationResponse::Json(resp))
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[instrument(skip_all)]
+pub async fn payment_method_intent_confirm(
+    state: &SessionState,
+    req: api::PaymentMethodIntentConfirm,
+    merchant_account: &domain::MerchantAccount,
+    key_store: &domain::MerchantKeyStore,
+    pm_id: String,
+) -> errors::RouterResponse<api::PaymentMethodResponse> {
+    req.validate()?;
+
+    let db = &*state.store;
+    let client_secret = req.client_secret.clone();
+    let pm_id = common_utils::id_type::GlobalPaymentMethodId::generate_from_string(pm_id)
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Unable to generate GlobalPaymentMethodId")?;
+
+    let payment_method = db
+        .find_payment_method(
+            &(state.into()),
+            &key_store,
+            &pm_id,
+            merchant_account.storage_scheme,
+        )
+        .await
+        .change_context(errors::ApiErrorResponse::PaymentMethodNotFound)
+        .attach_printable("Unable to find payment method")?;
+
+    when(
+        cards::authenticate_pm_client_secret_and_check_expiry(&client_secret, &payment_method)?,
+        || Err(errors::ApiErrorResponse::ClientSecretExpired),
+    )?;
+
+    when(
+        payment_method.status != enums::PaymentMethodStatus::AwaitingData,
+        || {
+            Err(errors::ApiErrorResponse::InvalidRequestData {
+                message: "Invalid pm_id provided: This Payment method cannot be confirmed"
+                    .to_string(),
+            })
+        },
+    )?;
+
+    let customer_id = payment_method.customer_id.to_owned();
+    db.find_customer_by_merchant_reference_id_merchant_id(
+        &(state.into()),
+        &customer_id,
+        merchant_account.get_id(),
+        &key_store,
+        merchant_account.storage_scheme,
+    )
+    .await
+    .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?;
+
+    let vaulting_result =
+        vault_payment_method(state, &req.payment_method_data, merchant_account, key_store).await;
+
+    let response = match vaulting_result {
+        Ok(resp) => {
+            let pm_update = create_pm_additional_data_update(
+                &req.payment_method_data,
+                state,
+                key_store,
+                Some(resp.vault_id),
+                Some(req.payment_method),
+                Some(req.payment_method_type),
+            )
+            .await
+            .attach_printable("Unable to create Payment method data")?;
+
+            let payment_method = db
+                .update_payment_method(
+                    &(state.into()),
+                    &key_store,
+                    payment_method,
+                    pm_update,
+                    merchant_account.storage_scheme,
+                )
+                .await
+                .change_context(errors::ApiErrorResponse::InternalServerError)
+                .attach_printable("Failed to update payment method in db")?;
+
+            let resp = pm_transforms::generate_payment_method_response(&payment_method)?;
+
+            Ok(resp)
+        }
+        Err(e) => {
+            let pm_update = storage::PaymentMethodUpdate::StatusUpdate {
+                status: Some(enums::PaymentMethodStatus::Inactive),
+            };
+
+            db.update_payment_method(
+                &(state.into()),
+                &key_store,
+                payment_method,
+                pm_update,
+                merchant_account.storage_scheme,
+            )
+            .await
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("Failed to update payment method in db")?;
+
+            Err(e)
+        }
+    }?;
+
+    Ok(services::ApplicationResponse::Json(response))
+}
+
+#[cfg(all(
+    feature = "v2",
+    feature = "payment_methods_v2",
+    feature = "customer_v2"
+))]
+#[instrument(skip_all)]
+#[allow(clippy::too_many_arguments)]
+pub async fn create_payment_method_in_db(
+    state: &SessionState,
+    req: &api::PaymentMethodCreate,
+    customer_id: &id_type::CustomerId,
+    payment_method_id: id_type::GlobalPaymentMethodId,
+    locker_id: Option<String>,
+    merchant_id: &id_type::MerchantId,
+    pm_metadata: Option<common_utils::pii::SecretSerdeValue>,
+    customer_acceptance: Option<common_utils::pii::SecretSerdeValue>,
+    payment_method_data: crypto::OptionalEncryptableValue,
+    key_store: &domain::MerchantKeyStore,
+    connector_mandate_details: Option<common_utils::pii::SecretSerdeValue>,
+    status: Option<enums::PaymentMethodStatus>,
+    network_transaction_id: Option<String>,
+    storage_scheme: enums::MerchantStorageScheme,
+    payment_method_billing_address: crypto::OptionalEncryptableValue,
+    card_scheme: Option<String>,
+) -> errors::CustomResult<domain::PaymentMethod, errors::ApiErrorResponse> {
+    let db = &*state.store;
+    let client_secret = pm_types::PaymentMethodClientSecret::generate(&payment_method_id);
+    let current_time = common_utils::date_time::now();
+
+    let response = db
+        .insert_payment_method(
+            &state.into(),
+            key_store,
+            domain::PaymentMethod {
+                customer_id: customer_id.to_owned(),
+                merchant_id: merchant_id.to_owned(),
+                id: payment_method_id,
+                locker_id,
+                payment_method: Some(req.payment_method),
+                payment_method_type: Some(req.payment_method_type),
+                metadata: pm_metadata,
+                payment_method_data,
+                connector_mandate_details,
+                customer_acceptance,
+                client_secret: Some(client_secret),
+                status: status.unwrap_or(enums::PaymentMethodStatus::Active),
+                network_transaction_id: network_transaction_id.to_owned(),
+                created_at: current_time,
+                last_modified: current_time,
+                last_used_at: current_time,
+                payment_method_billing_address,
+                updated_by: None,
+                version: domain::consts::API_VERSION,
+                locker_fingerprint_id: None,
+                network_token_locker_id: None,
+                network_token_payment_method_data: None,
+                network_token_requestor_reference_id: None,
+            },
+            storage_scheme,
+        )
+        .await
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Failed to add payment method in db")?;
+
+    Ok(response)
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[instrument(skip_all)]
+#[allow(clippy::too_many_arguments)]
+pub async fn create_payment_method_for_intent(
+    state: &SessionState,
+    metadata: Option<common_utils::pii::SecretSerdeValue>,
+    customer_id: &id_type::CustomerId,
+    payment_method_id: id_type::GlobalPaymentMethodId,
+    merchant_id: &id_type::MerchantId,
+    key_store: &domain::MerchantKeyStore,
+    storage_scheme: enums::MerchantStorageScheme,
+    payment_method_billing_address: crypto::OptionalEncryptableValue,
+) -> errors::CustomResult<domain::PaymentMethod, errors::ApiErrorResponse> {
+    let db = &*state.store;
+    let client_secret = pm_types::PaymentMethodClientSecret::generate(&payment_method_id);
+    let current_time = common_utils::date_time::now();
+
+    let response = db
+        .insert_payment_method(
+            &state.into(),
+            key_store,
+            domain::PaymentMethod {
+                customer_id: customer_id.to_owned(),
+                merchant_id: merchant_id.to_owned(),
+                id: payment_method_id,
+                locker_id: None,
+                payment_method: None,
+                payment_method_type: None,
+                metadata,
+                payment_method_data: None,
+                connector_mandate_details: None,
+                customer_acceptance: None,
+                client_secret: Some(client_secret),
+                status: enums::PaymentMethodStatus::AwaitingData,
+                network_transaction_id: None,
+                created_at: current_time,
+                last_modified: current_time,
+                last_used_at: current_time,
+                payment_method_billing_address,
+                updated_by: None,
+                version: domain::consts::API_VERSION,
+                locker_fingerprint_id: None,
+                network_token_locker_id: None,
+                network_token_payment_method_data: None,
+                network_token_requestor_reference_id: None,
+            },
+            storage_scheme,
+        )
+        .await
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Failed to add payment method in db")?;
+
+    Ok(response)
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+pub async fn create_pm_additional_data_update(
+    pmd: &api::PaymentMethodCreateData,
+    state: &SessionState,
+    key_store: &domain::MerchantKeyStore,
+    vault_id: Option<String>,
+    payment_method: Option<api_enums::PaymentMethod>,
+    payment_method_type: Option<api_enums::PaymentMethodType>,
+) -> errors::RouterResult<storage::PaymentMethodUpdate> {
+    let card = match pmd.clone() {
+        api::PaymentMethodCreateData::Card(card) => api::PaymentMethodsData::Card(card.into()),
+    };
+
+    let pmd: Encryptable<Secret<serde_json::Value>> =
+        cards::create_encrypted_data(state, key_store, card)
+            .await
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("Unable to encrypt Payment method data")?;
+
+    let pm_update = storage::PaymentMethodUpdate::AdditionalDataUpdate {
+        status: Some(enums::PaymentMethodStatus::Active),
+        locker_id: vault_id,
+        payment_method,
+        payment_method_type,
+        payment_method_data: Some(pmd).map(Into::into),
+        network_token_requestor_reference_id: None,
+        network_token_locker_id: None,
+        network_token_payment_method_data: None,
+    };
+
+    Ok(pm_update)
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[instrument(skip_all)]
+pub async fn vault_payment_method(
+    state: &SessionState,
+    pmd: &api::PaymentMethodCreateData,
+    merchant_account: &domain::MerchantAccount,
+    key_store: &domain::MerchantKeyStore,
+) -> errors::RouterResult<pm_types::AddVaultResponse> {
+    let db = &*state.store;
+
+    // get fingerprint_id from locker
+    let fingerprint_id_from_locker = cards::get_fingerprint_id_from_locker(state, pmd)
+        .await
+        .attach_printable("Failed to get fingerprint_id from vault")?;
+
+    // throw back error if payment method is duplicated
+    when(
+        Some(
+            db.find_payment_method_by_fingerprint_id(
+                &(state.into()),
+                key_store,
+                &fingerprint_id_from_locker,
+            )
+            .await
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("Failed to find payment method by fingerprint_id")?,
+        )
+        .is_some(),
+        || {
+            Err(report!(errors::ApiErrorResponse::DuplicatePaymentMethod)
+                .attach_printable("Cannot vault duplicate payment method"))
+        },
+    )?;
+
+    let resp_from_locker =
+        cards::vault_payment_method_in_locker(state, merchant_account, pmd).await?;
+
+    Ok(resp_from_locker)
+}
+
+#[cfg(all(
+    feature = "v2",
+    feature = "payment_methods_v2",
+    feature = "customer_v2"
+))]
+async fn get_pm_list_context(
+    state: &SessionState,
+    payment_method: &enums::PaymentMethod,
+    _key_store: &domain::MerchantKeyStore,
+    pm: &domain::PaymentMethod,
+    _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 = cards::get_card_details_with_locker_fallback(pm, state).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(
+                    storage::PaymentTokenData::permanent_card(
+                        Some(pm.get_id().clone()),
+                        pm.locker_id.clone().or(Some(pm.get_id().get_string_repr())),
+                        pm.locker_id
+                            .clone()
+                            .unwrap_or(pm.get_id().get_string_repr()),
+                    ),
+                ),
+            })
+        }
+
+        enums::PaymentMethod::BankDebit => {
+            // Retrieve the pm_auth connector details so that it can be tokenized
+            let bank_account_token_data = cards::get_bank_account_connector_details(pm)
+                .await
+                .unwrap_or_else(|err| {
+                    logger::error!(error=?err);
+                    None
+                });
+
+            bank_account_token_data.map(|data| {
+                let token_data = storage::PaymentTokenData::AuthBankDebit(data);
+
+                PaymentMethodListContext {
+                    card_details: None,
+                    #[cfg(feature = "payouts")]
+                    bank_transfer_details: None,
+                    hyperswitch_token_data: is_payment_associated.then_some(token_data),
+                }
+            })
+        }
+
+        _ => Some(PaymentMethodListContext {
+            card_details: None,
+            #[cfg(feature = "payouts")]
+            bank_transfer_details: None,
+            hyperswitch_token_data: is_payment_associated.then_some(
+                storage::PaymentTokenData::temporary_generic(generate_id(
+                    consts::ID_LENGTH,
+                    "token",
+                )),
+            ),
+        }),
+    };
+
+    Ok(payment_method_retrieval_context)
+}
+
+#[cfg(all(
+    feature = "v2",
+    feature = "payment_methods_v2",
+    feature = "customer_v2"
+))]
+pub async fn list_customer_payment_method_util(
+    state: 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 = payment_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(
+    feature = "v2",
+    feature = "payment_methods_v2",
+    feature = "customer_v2"
+))]
+pub async fn list_customer_payment_method(
+    state: &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_merchant_reference_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| {
+            pm_types::SavedPMLPaymentsInfo::form_payments_info(
+                pi,
+                &merchant_account,
+                db,
+                key_manager_state,
+                &key_store,
+            )
+        })
+        .await
+        .transpose()?;
+
+    let saved_payment_methods = db
+        .find_payment_method_by_customer_id_merchant_id_status(
+            key_manager_state,
+            &key_store,
+            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(cards::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: &SessionState,
+    key_store: &domain::MerchantKeyStore,
+    merchant_account: &domain::MerchantAccount,
+    pm_list_context: (
+        PaymentMethodListContext,
+        Option<String>,
+        domain::PaymentMethod,
+    ),
+    customer: &domain::Customer,
+    payment_info: Option<&pm_types::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 {
+        cards::get_masked_bank_details(&pm)
+            .await
+            .unwrap_or_else(|err| {
+                logger::error!(error=?err);
+                None
+            })
+    } else {
+        None
+    };
+
+    let payment_method_billing = pm
+        .payment_method_billing_address
+        .clone()
+        .map(|decrypted_data| decrypted_data.into_inner().expose())
+        .map(|decrypted_value| decrypted_value.parse_value("payment_method_billing_address"))
+        .transpose()
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("unable to parse 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, profile_id) =
+        payment_info
+            .map(|pi| {
+                (
+                    pi.is_connector_agnostic_mit_enabled,
+                    pi.requires_cvv,
+                    pi.off_session_payment_flag,
+                    pi.business_profile
+                        .as_ref()
+                        .map(|profile| profile.get_id().to_owned()),
+                )
+            })
+            .unwrap_or((false, false, false, Default::default()));
+
+    let mca_enabled = cards::get_mca_status(
+        state,
+        key_store,
+        profile_id,
+        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.get_id().get_string_repr(),
+        customer_id: pm.customer_id.to_owned(),
+        payment_method,
+        payment_method_type: pm.payment_method_type,
+        payment_method_data: pmd,
+        metadata: pm.metadata.clone(),
+        recurring_enabled: mca_enabled,
+        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),
+        is_default: customer.default_payment_method_id.is_some()
+            && customer.default_payment_method_id.as_ref() == Some(&pm.get_id().get_string_repr()),
+        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)
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+impl pm_types::SavedPMLPaymentsInfo {
+    pub async fn form_payments_info(
+        payment_intent: storage::PaymentIntent,
+        merchant_account: &domain::MerchantAccount,
+        db: &dyn StorageInterface,
+        key_manager_state: &util_types::keymanager::KeyManagerState,
+        key_store: &domain::MerchantKeyStore,
+    ) -> errors::RouterResult<Self> {
+        let requires_cvv = db
+            .find_config_by_key_unwrap_or(
+                format!(
+                    "{}_requires_cvv",
+                    merchant_account.get_id().get_string_repr()
+                )
+                .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 = payment_intent
+            .profile_id
+            .as_ref()
+            .get_required_value("profile_id")
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("profile_id is not set in payment_intent")?
+            .clone();
+
+        let business_profile = core_utils::validate_and_get_business_profile(
+            db,
+            key_manager_state,
+            key_store,
+            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: &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.get_order_fulfillment_time())
+            .unwrap_or(common_utils::consts::DEFAULT_INTENT_FULFILLMENT_TIME);
+
+        pm_routes::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: pm_transforms::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(())
+    }
+}
diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs
index 22350f77e69..79d1226f13a 100644
--- a/crates/router/src/core/payment_methods/cards.rs
+++ b/crates/router/src/core/payment_methods/cards.rs
@@ -69,8 +69,6 @@ use super::surcharge_decision_configs::{
 use crate::routes::app::SessionStateInfo;
 #[cfg(feature = "payouts")]
 use crate::types::domain::types::AsyncLift;
-#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))]
-use crate::utils::{self};
 use crate::{
     configs::{
         defaults::{get_billing_required_fields, get_shipping_required_fields},
@@ -95,8 +93,14 @@ use crate::{
         storage::{self, enums, PaymentMethodListContext, PaymentTokenData},
         transformers::ForeignTryFrom,
     },
+    utils,
     utils::OptionExt,
 };
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+use crate::{
+    consts as router_consts, core::payment_methods as pm_core, headers,
+    types::payment_methods as pm_types, utils::ConnectorResponseExt,
+};
 #[cfg(all(
     any(feature = "v1", feature = "v2"),
     not(feature = "payment_methods_v2")
@@ -217,32 +221,35 @@ pub async fn create_payment_method(
     Ok(response)
 }
 
-#[cfg(all(
-    feature = "v2",
-    feature = "payment_methods_v2",
-    feature = "customer_v2"
-))]
-#[instrument(skip_all)]
-#[allow(clippy::too_many_arguments)]
-pub async fn create_payment_method(
-    _state: &routes::SessionState,
-    _req: &api::PaymentMethodCreate,
-    _customer_id: &id_type::CustomerId,
-    _payment_method_id: &str,
-    _locker_id: Option<String>,
-    _merchant_id: &id_type::MerchantId,
-    _pm_metadata: Option<serde_json::Value>,
-    _customer_acceptance: Option<serde_json::Value>,
-    _payment_method_data: Option<Encryption>,
-    _key_store: &domain::MerchantKeyStore,
-    _connector_mandate_details: Option<serde_json::Value>,
-    _status: Option<enums::PaymentMethodStatus>,
-    _network_transaction_id: Option<String>,
-    _storage_scheme: MerchantStorageScheme,
-    _payment_method_billing_address: Option<Encryption>,
-    _card_scheme: Option<String>,
-) -> errors::CustomResult<domain::PaymentMethod, errors::ApiErrorResponse> {
-    todo!()
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+async fn create_vault_request<R: pm_types::VaultingInterface>(
+    jwekey: &settings::Jwekey,
+    locker: &settings::Locker,
+    payload: Vec<u8>,
+) -> errors::CustomResult<services::Request, errors::VaultError> {
+    let private_key = jwekey.vault_private_key.peek().as_bytes();
+
+    let jws = services::encryption::jws_sign_payload(
+        &payload,
+        &locker.locker_signing_key_id,
+        private_key,
+    )
+    .await
+    .change_context(errors::VaultError::RequestEncryptionFailed)?;
+
+    let jwe_payload = payment_methods::create_jwe_body_for_vault(jwekey, &jws).await?;
+
+    let mut url = locker.host.to_owned();
+    url.push_str(R::get_vaulting_request_url());
+    let mut request = services::Request::new(services::Method::Post, &url);
+    request.add_header(
+        headers::CONTENT_TYPE,
+        router_consts::VAULT_HEADER_CONTENT_TYPE.into(),
+    );
+    request.set_body(common_utils::request::RequestContent::Json(Box::new(
+        jwe_payload,
+    )));
+    Ok(request)
 }
 
 #[cfg(all(
@@ -911,17 +918,6 @@ pub async fn get_client_secret_or_add_payment_method(
     }
 }
 
-#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
-#[instrument(skip_all)]
-pub async fn get_client_secret_or_add_payment_method(
-    _state: &routes::SessionState,
-    _req: api::PaymentMethodCreate,
-    _merchant_account: &domain::MerchantAccount,
-    _key_store: &domain::MerchantKeyStore,
-) -> errors::RouterResponse<api::PaymentMethodResponse> {
-    todo!()
-}
-
 #[instrument(skip_all)]
 pub fn authenticate_pm_client_secret_and_check_expiry(
     req_client_secret: &String,
@@ -1417,13 +1413,64 @@ pub async fn add_payment_method(
 
 #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
 #[instrument(skip_all)]
-pub async fn add_payment_method(
-    _state: &routes::SessionState,
-    _req: api::PaymentMethodCreate,
-    _merchant_account: &domain::MerchantAccount,
-    _key_store: &domain::MerchantKeyStore,
-) -> errors::RouterResponse<api::PaymentMethodResponse> {
-    todo!()
+pub async fn get_fingerprint_id_from_locker<
+    D: pm_types::VaultingDataInterface + serde::Serialize,
+>(
+    state: &routes::SessionState,
+    data: &D,
+) -> errors::RouterResult<String> {
+    let key = data.get_vaulting_data_key();
+    let data = serde_json::to_value(data)
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Failed to encode Vaulting data to value")?
+        .to_string();
+
+    let payload = pm_types::VaultFingerprintRequest { key, data }
+        .encode_to_vec()
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Failed to encode VaultFingerprintRequest")?;
+
+    let resp = call_to_vault::<pm_types::GetVaultFingerprint>(state, payload)
+        .await
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Failed to get response from locker")?;
+
+    let fingerprint_resp: pm_types::VaultFingerprintResponse = resp
+        .parse_struct("VaultFingerprintResp")
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Failed to parse data into VaultFingerprintResp")?;
+
+    Ok(fingerprint_resp.fingerprint_id)
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[instrument(skip_all)]
+pub async fn vault_payment_method_in_locker(
+    state: &routes::SessionState,
+    merchant_account: &domain::MerchantAccount,
+    pmd: &api::PaymentMethodCreateData,
+) -> errors::RouterResult<pm_types::AddVaultResponse> {
+    let payload = pm_types::AddVaultRequest {
+        entity_id: merchant_account.get_id().to_owned(),
+        vault_id: uuid::Uuid::now_v7().to_string(),
+        data: pmd.clone(),
+        ttl: state.conf.locker.ttl_for_storage_in_secs,
+    }
+    .encode_to_vec()
+    .change_context(errors::ApiErrorResponse::InternalServerError)
+    .attach_printable("Failed to encode AddVaultRequest")?;
+
+    let resp = call_to_vault::<pm_types::AddVault>(state, payload)
+        .await
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Failed to get response from locker")?;
+
+    let stored_pm_resp: pm_types::AddVaultResponse = resp
+        .parse_struct("AddVaultResponse")
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Failed to parse data into AddVaultResponse")?;
+
+    Ok(stored_pm_resp)
 }
 
 #[cfg(all(
@@ -1506,40 +1553,7 @@ pub async fn insert_payment_method(
     storage_scheme: MerchantStorageScheme,
     payment_method_billing_address: Option<Encryption>,
 ) -> errors::RouterResult<domain::PaymentMethod> {
-    let pm_card_details = match &resp.payment_method_data {
-        Some(api::PaymentMethodResponseData::Card(card_data)) => Some(PaymentMethodsData::Card(
-            CardDetailsPaymentMethod::from(card_data.clone()),
-        )),
-        _ => None,
-    };
-
-    let pm_data_encrypted: Option<Encryptable<Secret<serde_json::Value>>> = pm_card_details
-        .clone()
-        .async_map(|pm_card| create_encrypted_data(state, key_store, pm_card))
-        .await
-        .transpose()
-        .change_context(errors::ApiErrorResponse::InternalServerError)
-        .attach_printable("Unable to encrypt payment method data")?;
-
-    create_payment_method(
-        state,
-        req,
-        customer_id,
-        &resp.payment_method_id,
-        locker_id,
-        merchant_id,
-        pm_metadata,
-        customer_acceptance,
-        pm_data_encrypted.map(Into::into),
-        key_store,
-        connector_mandate_details,
-        None,
-        network_transaction_id,
-        storage_scheme,
-        payment_method_billing_address,
-        None,
-    )
-    .await
+    todo!()
 }
 
 #[cfg(all(
@@ -2225,6 +2239,37 @@ pub async fn add_card_to_hs_locker(
     Ok(stored_card)
 }
 
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[instrument(skip_all)]
+pub async fn call_to_vault<V: pm_types::VaultingInterface>(
+    state: &routes::SessionState,
+    payload: Vec<u8>,
+) -> errors::CustomResult<String, errors::VaultError> {
+    let locker = &state.conf.locker;
+    let jwekey = state.conf.jwekey.get_inner();
+
+    let request = create_vault_request::<V>(jwekey, locker, payload).await?;
+    let response = services::call_connector_api(state, request, "vault_in_locker")
+        .await
+        .change_context(errors::VaultError::VaultAPIError);
+
+    let jwe_body: services::JweBody = response
+        .get_response_inner("JweBody")
+        .change_context(errors::VaultError::ResponseDeserializationFailed)
+        .attach_printable("Failed to get JweBody from vault response")?;
+
+    let decrypted_payload = payment_methods::get_decrypted_vault_response_payload(
+        jwekey,
+        jwe_body,
+        locker.decryption_scheme.clone(),
+    )
+    .await
+    .change_context(errors::VaultError::ResponseDecryptionFailed)
+    .attach_printable("Error getting decrypted vault response payload")?;
+
+    Ok(decrypted_payload)
+}
+
 #[instrument(skip_all)]
 pub async fn call_locker_api<T>(
     state: &routes::SessionState,
@@ -3819,6 +3864,10 @@ fn should_collect_shipping_or_billing_details_from_wallet_connector(
     }
 }
 
+#[cfg(all(
+    any(feature = "v1", feature = "v2"),
+    not(feature = "payment_methods_v2"),
+))]
 async fn validate_payment_method_and_client_secret(
     state: &routes::SessionState,
     cs: &String,
@@ -4235,63 +4284,6 @@ fn filter_recurring_based(
     recurring_enabled.map_or(true, |enabled| payment_method.recurring_enabled == enabled)
 }
 
-#[cfg(all(
-    feature = "v2",
-    feature = "payment_methods_v2",
-    feature = "customer_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"),
@@ -4615,6 +4607,11 @@ pub async fn list_customer_payment_method(
     Ok(services::ApplicationResponse::Json(response))
 }
 
+#[cfg(all(
+    any(feature = "v2", feature = "v1"),
+    not(feature = "payment_methods_v2"),
+    not(feature = "customer_v2")
+))]
 async fn get_pm_list_context(
     state: &routes::SessionState,
     payment_method: &enums::PaymentMethod,
@@ -4706,7 +4703,11 @@ async fn get_pm_list_context(
     Ok(payment_method_retrieval_context)
 }
 
-#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))]
+#[cfg(all(
+    any(feature = "v1", feature = "v2"),
+    not(feature = "payment_v2"),
+    not(feature = "payment_methods_v2")
+))]
 async fn perform_surcharge_ops(
     payment_intent: Option<storage::PaymentIntent>,
     state: &routes::SessionState,
@@ -4751,8 +4752,8 @@ async fn perform_surcharge_ops(
     Ok(())
 }
 
-#[cfg(all(feature = "v2", feature = "payment_v2"))]
-async fn perform_surcharge_ops(
+#[cfg(all(feature = "v2", feature = "payment_v2", feature = "payment_methods_v2"))]
+pub async fn perform_surcharge_ops(
     _payment_intent: Option<storage::PaymentIntent>,
     _state: &routes::SessionState,
     _merchant_account: domain::MerchantAccount,
@@ -4763,370 +4764,6 @@ async fn perform_surcharge_ops(
     todo!()
 }
 
-#[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,
-        key_manager_state: &KeyManagerState,
-        key_store: &domain::MerchantKeyStore,
-    ) -> errors::RouterResult<Self> {
-        let requires_cvv = db
-            .find_config_by_key_unwrap_or(
-                format!(
-                    "{}_requires_cvv",
-                    merchant_account.get_id().get_string_repr()
-                )
-                .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 = payment_intent
-            .profile_id
-            .as_ref()
-            .get_required_value("profile_id")
-            .change_context(errors::ApiErrorResponse::InternalServerError)
-            .attach_printable("profile_id is not set in payment_intent")?
-            .clone();
-
-        let business_profile = core_utils::validate_and_get_business_profile(
-            db,
-            key_manager_state,
-            key_store,
-            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.get_order_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",
-    feature = "customer_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_merchant_reference_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,
-                key_manager_state,
-                &key_store,
-            )
-        })
-        .await
-        .transpose()?;
-
-    let saved_payment_methods = db
-        .find_payment_method_by_customer_id_merchant_id_status(
-            key_manager_state,
-            &key_store,
-            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() {
-        logger::debug!(
-            "Fetching payment method from locker for payment_method_id: {}",
-            pm.id
-        );
-        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>,
-        domain::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(&pm).await.unwrap_or_else(|err| {
-            logger::error!(error=?err);
-            None
-        })
-    } else {
-        None
-    };
-
-    let payment_method_billing = pm
-        .payment_method_billing_address
-        .clone()
-        .map(|decrypted_data| decrypted_data.into_inner().expose())
-        .map(|decrypted_value| decrypted_value.parse_value("payment_method_billing_address"))
-        .transpose()
-        .change_context(errors::ApiErrorResponse::InternalServerError)
-        .attach_printable("unable to parse 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, profile_id) =
-        payment_info
-            .map(|pi| {
-                (
-                    pi.is_connector_agnostic_mit_enabled,
-                    pi.requires_cvv,
-                    pi.off_session_payment_flag,
-                    pi.business_profile
-                        .as_ref()
-                        .map(|profile| profile.get_id().to_owned()),
-                )
-            })
-            .unwrap_or((false, false, false, Default::default()));
-
-    let mca_enabled = get_mca_status(
-        state,
-        key_store,
-        profile_id,
-        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.get_id().clone(),
-        customer_id: pm.customer_id.to_owned(),
-        payment_method,
-        payment_method_type: pm.payment_method_type,
-        payment_method_data: pmd,
-        metadata: pm.metadata.clone(),
-        recurring_enabled: mca_enabled,
-        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),
-        is_default: customer.default_payment_method_id.is_some()
-            && customer.default_payment_method_id.as_ref() == Some(pm.get_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,
@@ -5236,24 +4873,7 @@ pub async fn get_card_details_with_locker_fallback(
     pm: &domain::PaymentMethod,
     state: &routes::SessionState,
 ) -> errors::RouterResult<Option<api::CardDetailFromLocker>> {
-    let card_decrypted = pm
-        .payment_method_data
-        .clone()
-        .map(|x| x.into_inner().expose())
-        .and_then(|v| serde_json::from_value::<PaymentMethodsData>(v).ok())
-        .and_then(|pmd| match pmd {
-            PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)),
-            _ => None,
-        });
-
-    Ok(if let Some(crd) = card_decrypted {
-        Some(crd)
-    } else {
-        logger::debug!(
-            "Getting card details from locker as it is not found in payment methods table"
-        );
-        Some(get_card_details_from_locker(state, pm).await?)
-    })
+    todo!()
 }
 
 #[cfg(all(
@@ -5290,25 +4910,13 @@ pub async fn get_card_details_without_locker_fallback(
     pm: &domain::PaymentMethod,
     state: &routes::SessionState,
 ) -> errors::RouterResult<api::CardDetailFromLocker> {
-    let card_decrypted = pm
-        .payment_method_data
-        .clone()
-        .map(|x| x.into_inner().expose())
-        .and_then(|v| serde_json::from_value::<PaymentMethodsData>(v).ok())
-        .and_then(|pmd| match pmd {
-            PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)),
-            _ => None,
-        });
-    Ok(if let Some(crd) = card_decrypted {
-        crd
-    } else {
-        logger::debug!(
-            "Getting card details from locker as it is not found in payment methods table"
-        );
-        get_card_details_from_locker(state, pm).await?
-    })
+    todo!()
 }
 
+#[cfg(all(
+    any(feature = "v2", feature = "v1"),
+    not(feature = "payment_methods_v2")
+))]
 pub async fn get_card_details_from_locker(
     state: &routes::SessionState,
     pm: &domain::PaymentMethod,
@@ -5328,6 +4936,10 @@ pub async fn get_card_details_from_locker(
         .attach_printable("Get Card Details Failed")
 }
 
+#[cfg(all(
+    any(feature = "v2", feature = "v1"),
+    not(feature = "payment_methods_v2")
+))]
 pub async fn get_lookup_key_from_locker(
     state: &routes::SessionState,
     payment_token: &str,
@@ -5348,7 +4960,7 @@ pub async fn get_lookup_key_from_locker(
     Ok(resp)
 }
 
-async fn get_masked_bank_details(
+pub async fn get_masked_bank_details(
     pm: &domain::PaymentMethod,
 ) -> errors::RouterResult<Option<MaskedBankDetails>> {
     let payment_method_data = pm
@@ -5377,7 +4989,7 @@ async fn get_masked_bank_details(
     }
 }
 
-async fn get_bank_account_connector_details(
+pub async fn get_bank_account_connector_details(
     pm: &domain::PaymentMethod,
 ) -> errors::RouterResult<Option<BankAccountTokenData>> {
     let payment_method_data = pm
@@ -5604,8 +5216,16 @@ pub async fn get_bank_from_hs_locker(
     }
 }
 
+#[cfg(all(
+    any(feature = "v2", feature = "v1"),
+    not(feature = "payment_methods_v2")
+))]
 pub struct TempLockerCardSupport;
 
+#[cfg(all(
+    any(feature = "v2", feature = "v1"),
+    not(feature = "payment_methods_v2")
+))]
 impl TempLockerCardSupport {
     #[instrument(skip_all)]
     async fn create_payment_method_data_in_temp_locker(
@@ -5761,53 +5381,7 @@ pub async fn retrieve_payment_method(
     key_store: domain::MerchantKeyStore,
     merchant_account: domain::MerchantAccount,
 ) -> errors::RouterResponse<api::PaymentMethodResponse> {
-    let db = state.store.as_ref();
-    let pm = db
-        .find_payment_method(
-            &((&state).into()),
-            &key_store,
-            &pm.payment_method_id,
-            merchant_account.storage_scheme,
-        )
-        .await
-        .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;
-
-    let card = if pm.payment_method == Some(enums::PaymentMethod::Card) {
-        let card_detail = if state.conf.locker.locker_enabled {
-            let card = get_card_from_locker(
-                &state,
-                &pm.customer_id,
-                &pm.merchant_id,
-                pm.locker_id.as_ref().unwrap_or(pm.get_id()),
-            )
-            .await
-            .change_context(errors::ApiErrorResponse::InternalServerError)
-            .attach_printable("Error getting card from card vault")?;
-            payment_methods::get_card_detail(&pm, card)
-                .change_context(errors::ApiErrorResponse::InternalServerError)
-                .attach_printable("Failed while getting card details from locker")?
-        } else {
-            get_card_details_without_locker_fallback(&pm, &state).await?
-        };
-        Some(card_detail)
-    } else {
-        None
-    };
-    Ok(services::ApplicationResponse::Json(
-        api::PaymentMethodResponse {
-            merchant_id: pm.merchant_id.to_owned(),
-            customer_id: pm.customer_id.to_owned(),
-            payment_method_id: pm.get_id().clone(),
-            payment_method: pm.payment_method,
-            payment_method_type: pm.payment_method_type,
-            metadata: pm.metadata,
-            created: Some(pm.created_at),
-            recurring_enabled: false,
-            payment_method_data: card.clone().map(api::PaymentMethodResponseData::Card),
-            last_used_at: Some(pm.last_used_at),
-            client_secret: pm.client_secret,
-        },
-    ))
+    todo!()
 }
 
 #[cfg(all(
diff --git a/crates/router/src/core/payment_methods/transformers.rs b/crates/router/src/core/payment_methods/transformers.rs
index 17477141492..d0f63a2dc04 100644
--- a/crates/router/src/core/payment_methods/transformers.rs
+++ b/crates/router/src/core/payment_methods/transformers.rs
@@ -2,6 +2,8 @@
 use std::str::FromStr;
 
 use api_models::{enums as api_enums, payment_methods::Card};
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+use common_utils::ext_traits::ValueExt;
 use common_utils::{
     ext_traits::{Encode, StringExt},
     id_type,
@@ -249,6 +251,92 @@ pub async fn get_decrypted_response_payload(
         .attach_printable("Jws Decryption failed for JwsBody for vault")
 }
 
+pub async fn get_decrypted_vault_response_payload(
+    jwekey: &settings::Jwekey,
+    jwe_body: encryption::JweBody,
+    decryption_scheme: settings::DecryptionScheme,
+) -> CustomResult<String, errors::VaultError> {
+    let public_key = jwekey.vault_encryption_key.peek().as_bytes();
+
+    let private_key = jwekey.vault_private_key.peek().as_bytes();
+
+    let jwt = get_dotted_jwe(jwe_body);
+    let alg = match decryption_scheme {
+        settings::DecryptionScheme::RsaOaep => jwe::RSA_OAEP,
+        settings::DecryptionScheme::RsaOaep256 => jwe::RSA_OAEP_256,
+    };
+
+    let jwe_decrypted = encryption::decrypt_jwe(
+        &jwt,
+        encryption::KeyIdCheck::SkipKeyIdCheck,
+        private_key,
+        alg,
+    )
+    .await
+    .change_context(errors::VaultError::SaveCardFailed)
+    .attach_printable("Jwe Decryption failed for JweBody for vault")?;
+
+    let jws = jwe_decrypted
+        .parse_struct("JwsBody")
+        .change_context(errors::VaultError::ResponseDeserializationFailed)?;
+    let jws_body = get_dotted_jws(jws);
+
+    encryption::verify_sign(jws_body, public_key)
+        .change_context(errors::VaultError::SaveCardFailed)
+        .attach_printable("Jws Decryption failed for JwsBody for vault")
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+pub async fn create_jwe_body_for_vault(
+    jwekey: &settings::Jwekey,
+    jws: &str,
+) -> CustomResult<encryption::JweBody, errors::VaultError> {
+    let jws_payload: Vec<&str> = jws.split('.').collect();
+
+    let generate_jws_body = |payload: Vec<&str>| -> Option<encryption::JwsBody> {
+        Some(encryption::JwsBody {
+            header: payload.first()?.to_string(),
+            payload: payload.get(1)?.to_string(),
+            signature: payload.get(2)?.to_string(),
+        })
+    };
+
+    let jws_body =
+        generate_jws_body(jws_payload).ok_or(errors::VaultError::RequestEncryptionFailed)?;
+
+    let payload = jws_body
+        .encode_to_vec()
+        .change_context(errors::VaultError::RequestEncodingFailed)?;
+
+    let public_key = jwekey.vault_encryption_key.peek().as_bytes();
+
+    let jwe_encrypted = encryption::encrypt_jwe(
+        &payload,
+        public_key,
+        encryption::EncryptionAlgorithm::A256GCM,
+        None,
+    )
+    .await
+    .change_context(errors::VaultError::SaveCardFailed)
+    .attach_printable("Error on jwe encrypt")?;
+    let jwe_payload: Vec<&str> = jwe_encrypted.split('.').collect();
+
+    let generate_jwe_body = |payload: Vec<&str>| -> Option<encryption::JweBody> {
+        Some(encryption::JweBody {
+            header: payload.first()?.to_string(),
+            iv: payload.get(2)?.to_string(),
+            encrypted_payload: payload.get(3)?.to_string(),
+            tag: payload.get(4)?.to_string(),
+            encrypted_key: payload.get(1)?.to_string(),
+        })
+    };
+
+    let jwe_body =
+        generate_jwe_body(jwe_payload).ok_or(errors::VaultError::RequestEncodingFailed)?;
+
+    Ok(jwe_body)
+}
+
 pub async fn mk_basilisk_req(
     jwekey: &settings::Jwekey,
     jws: &str,
@@ -428,14 +516,50 @@ pub fn mk_add_card_response_hs(
 
 #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
 pub fn mk_add_card_response_hs(
-    _card: api::CardDetail,
-    _card_reference: String,
-    _req: api::PaymentMethodCreate,
-    _merchant_id: &id_type::MerchantId,
+    card: api::CardDetail,
+    card_reference: String,
+    req: api::PaymentMethodCreate,
+    merchant_id: &id_type::MerchantId,
 ) -> api::PaymentMethodResponse {
     todo!()
 }
 
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+pub fn generate_payment_method_response(
+    pm: &domain::PaymentMethod,
+) -> errors::RouterResult<api::PaymentMethodResponse> {
+    let pmd = pm
+        .payment_method_data
+        .clone()
+        .map(|data| data.into_inner().expose())
+        .map(|decrypted_value| decrypted_value.parse_value("PaymentMethodsData"))
+        .transpose()
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("unable to parse PaymentMethodsData")?
+        .and_then(|data| match data {
+            api::PaymentMethodsData::Card(card) => {
+                Some(api::PaymentMethodResponseData::Card(card.into()))
+            }
+            _ => None,
+        });
+
+    let resp = api::PaymentMethodResponse {
+        merchant_id: pm.merchant_id.to_owned(),
+        customer_id: pm.customer_id.to_owned(),
+        payment_method_id: pm.id.get_string_repr(),
+        payment_method: pm.payment_method,
+        payment_method_type: pm.payment_method_type,
+        metadata: pm.metadata.clone(),
+        created: Some(pm.created_at),
+        recurring_enabled: false,
+        last_used_at: Some(pm.last_used_at),
+        client_secret: pm.client_secret.clone(),
+        payment_method_data: pmd,
+    };
+
+    Ok(resp)
+}
+
 #[allow(clippy::too_many_arguments)]
 pub async fn mk_get_card_request_hs(
     jwekey: &settings::Jwekey,
diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs
index 0ec14bd69ed..0af5bf6037a 100644
--- a/crates/router/src/core/payments.rs
+++ b/crates/router/src/core/payments.rs
@@ -3807,6 +3807,25 @@ where
     .await
 }
 
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+pub async fn decide_multiplex_connector_for_normal_or_recurring_payment<F: Clone, D>(
+    state: &SessionState,
+    payment_data: &mut D,
+    routing_data: &mut storage::RoutingData,
+    connectors: Vec<api::ConnectorData>,
+    mandate_type: Option<api::MandateTransactionType>,
+    is_connector_agnostic_mit_enabled: Option<bool>,
+) -> RouterResult<ConnectorCallType>
+where
+    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
+{
+    todo!()
+}
+
+#[cfg(all(
+    any(feature = "v2", feature = "v1"),
+    not(feature = "payment_methods_v2")
+))]
 #[allow(clippy::too_many_arguments)]
 pub async fn decide_multiplex_connector_for_normal_or_recurring_payment<F: Clone, D>(
     state: &SessionState,
@@ -3970,6 +3989,26 @@ where
     }
 }
 
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[allow(clippy::too_many_arguments)]
+pub async fn decide_connector_for_normal_or_recurring_payment<F: Clone, D>(
+    state: &SessionState,
+    payment_data: &mut D,
+    routing_data: &mut storage::RoutingData,
+    connectors: Vec<api::ConnectorData>,
+    is_connector_agnostic_mit_enabled: Option<bool>,
+    payment_method_info: &domain::PaymentMethod,
+) -> RouterResult<ConnectorCallType>
+where
+    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
+{
+    todo!()
+}
+
+#[cfg(all(
+    any(feature = "v2", feature = "v1"),
+    not(feature = "payment_methods_v2")
+))]
 #[allow(clippy::too_many_arguments)]
 pub async fn decide_connector_for_normal_or_recurring_payment<F: Clone, D>(
     state: &SessionState,
diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs
index a1d92d8a992..ce92c1ab007 100644
--- a/crates/router/src/core/payments/helpers.rs
+++ b/crates/router/src/core/payments/helpers.rs
@@ -1781,6 +1781,23 @@ pub async fn retrieve_payment_method_with_temporary_token(
     })
 }
 
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+pub async fn retrieve_card_with_permanent_token(
+    state: &SessionState,
+    locker_id: &str,
+    _payment_method_id: &common_utils::id_type::GlobalPaymentMethodId,
+    payment_intent: &PaymentIntent,
+    card_token_data: Option<&domain::CardToken>,
+    _merchant_key_store: &domain::MerchantKeyStore,
+    _storage_scheme: enums::MerchantStorageScheme,
+) -> RouterResult<domain::PaymentMethodData> {
+    todo!()
+}
+
+#[cfg(all(
+    any(feature = "v2", feature = "v1"),
+    not(feature = "payment_methods_v2")
+))]
 #[allow(clippy::too_many_arguments)]
 pub async fn retrieve_card_with_permanent_token(
     state: &SessionState,
@@ -1986,6 +2003,20 @@ pub async fn fetch_card_details_from_locker(
     Ok(domain::PaymentMethodData::Card(api_card.into()))
 }
 
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+pub async fn retrieve_payment_method_from_db_with_token_data(
+    state: &SessionState,
+    merchant_key_store: &domain::MerchantKeyStore,
+    token_data: &storage::PaymentTokenData,
+    storage_scheme: storage::enums::MerchantStorageScheme,
+) -> RouterResult<Option<domain::PaymentMethod>> {
+    todo!()
+}
+
+#[cfg(all(
+    any(feature = "v2", feature = "v1"),
+    not(feature = "payment_methods_v2")
+))]
 pub async fn retrieve_payment_method_from_db_with_token_data(
     state: &SessionState,
     merchant_key_store: &domain::MerchantKeyStore,
@@ -2082,6 +2113,27 @@ pub async fn retrieve_payment_token_data(
     Ok(token_data)
 }
 
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+pub async fn make_pm_data<'a, F: Clone, R, D>(
+    _operation: BoxedOperation<'a, F, R, D>,
+    _state: &'a SessionState,
+    _payment_data: &mut PaymentData<F>,
+    _merchant_key_store: &domain::MerchantKeyStore,
+    _customer: &Option<domain::Customer>,
+    _storage_scheme: common_enums::enums::MerchantStorageScheme,
+    _business_profile: Option<&domain::BusinessProfile>,
+) -> RouterResult<(
+    BoxedOperation<'a, F, R, D>,
+    Option<domain::PaymentMethodData>,
+    Option<String>,
+)> {
+    todo!()
+}
+
+#[cfg(all(
+    any(feature = "v1", feature = "v2"),
+    not(feature = "payment_methods_v2")
+))]
 pub async fn make_pm_data<'a, F: Clone, R, D>(
     operation: BoxedOperation<'a, F, R, D>,
     state: &'a SessionState,
@@ -5100,6 +5152,21 @@ pub fn update_additional_payment_data_with_connector_response_pm_data(
         .attach_printable("Failed to encode additional pm data")
 }
 
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+pub async fn get_payment_method_details_from_payment_token(
+    state: &SessionState,
+    payment_attempt: &PaymentAttempt,
+    payment_intent: &PaymentIntent,
+    key_store: &domain::MerchantKeyStore,
+    storage_scheme: enums::MerchantStorageScheme,
+) -> RouterResult<Option<(domain::PaymentMethodData, enums::PaymentMethod)>> {
+    todo!()
+}
+
+#[cfg(all(
+    any(feature = "v1", feature = "v2"),
+    not(feature = "payment_methods_v2")
+))]
 pub async fn get_payment_method_details_from_payment_token(
     state: &SessionState,
     payment_attempt: &PaymentAttempt,
diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs
index f397ecd8a2a..a9cd1ae4d84 100644
--- a/crates/router/src/core/payments/operations/payment_create.rs
+++ b/crates/router/src/core/payments/operations/payment_create.rs
@@ -978,6 +978,35 @@ impl<F: Send + Clone> ValidateRequest<F, api::PaymentsRequest, PaymentData<F>> f
 }
 
 impl PaymentCreate {
+    #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+    #[instrument(skip_all)]
+    #[allow(clippy::too_many_arguments)]
+    pub async fn make_payment_attempt(
+        payment_id: &common_utils::id_type::PaymentId,
+        merchant_id: &common_utils::id_type::MerchantId,
+        organization_id: &common_utils::id_type::OrganizationId,
+        money: (api::Amount, enums::Currency),
+        payment_method: Option<enums::PaymentMethod>,
+        payment_method_type: Option<enums::PaymentMethodType>,
+        request: &api::PaymentsRequest,
+        browser_info: Option<serde_json::Value>,
+        state: &SessionState,
+        payment_method_billing_address_id: Option<String>,
+        payment_method_info: &Option<domain::PaymentMethod>,
+        _key_store: &domain::MerchantKeyStore,
+        profile_id: common_utils::id_type::ProfileId,
+        customer_acceptance: &Option<payments::CustomerAcceptance>,
+    ) -> RouterResult<(
+        storage::PaymentAttemptNew,
+        Option<api_models::payments::AdditionalPaymentData>,
+    )> {
+        todo!()
+    }
+
+    #[cfg(all(
+        any(feature = "v1", feature = "v2"),
+        not(feature = "payment_methods_v2")
+    ))]
     #[instrument(skip_all)]
     #[allow(clippy::too_many_arguments)]
     pub async fn make_payment_attempt(
diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs
index fb09b4d8b60..ae6ac330679 100644
--- a/crates/router/src/core/payments/operations/payment_response.rs
+++ b/crates/router/src/core/payments/operations/payment_response.rs
@@ -88,6 +88,26 @@ impl<F: Send + Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsAuthor
         Ok(payment_data)
     }
 
+    #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+    async fn save_pm_and_mandate<'b>(
+        &self,
+        state: &SessionState,
+        resp: &types::RouterData<F, types::PaymentsAuthorizeData, types::PaymentsResponseData>,
+        merchant_account: &domain::MerchantAccount,
+        key_store: &domain::MerchantKeyStore,
+        payment_data: &mut PaymentData<F>,
+        business_profile: &domain::BusinessProfile,
+    ) -> CustomResult<(), errors::ApiErrorResponse>
+    where
+        F: 'b + Clone + Send + Sync,
+    {
+        todo!()
+    }
+
+    #[cfg(all(
+        any(feature = "v2", feature = "v1"),
+        not(feature = "payment_methods_v2")
+    ))]
     async fn save_pm_and_mandate<'b>(
         &self,
         state: &SessionState,
@@ -1602,6 +1622,23 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
     }
 }
 
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+async fn update_payment_method_status_and_ntid<F: Clone>(
+    state: &SessionState,
+    key_store: &domain::MerchantKeyStore,
+    payment_data: &mut PaymentData<F>,
+    attempt_status: common_enums::AttemptStatus,
+    payment_response: Result<types::PaymentsResponseData, ErrorResponse>,
+    storage_scheme: enums::MerchantStorageScheme,
+    is_connector_agnostic_mit_enabled: Option<bool>,
+) -> RouterResult<()> {
+    todo!()
+}
+
+#[cfg(all(
+    any(feature = "v2", feature = "v1"),
+    not(feature = "payment_methods_v2")
+))]
 async fn update_payment_method_status_and_ntid<F: Clone>(
     state: &SessionState,
     key_store: &domain::MerchantKeyStore,
diff --git a/crates/router/src/core/payments/operations/payment_status.rs b/crates/router/src/core/payments/operations/payment_status.rs
index d81258d6671..0760cd449fe 100644
--- a/crates/router/src/core/payments/operations/payment_status.rs
+++ b/crates/router/src/core/payments/operations/payment_status.rs
@@ -214,6 +214,28 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRetrieveRequest
     }
 }
 
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+async fn get_tracker_for_sync<
+    'a,
+    F: Send + Clone,
+    Op: Operation<F, api::PaymentsRetrieveRequest, Data = PaymentData<F>> + 'a + Send + Sync,
+>(
+    _payment_id: &api::PaymentIdType,
+    _merchant_account: &domain::MerchantAccount,
+    _key_store: &domain::MerchantKeyStore,
+    _state: &SessionState,
+    _request: &api::PaymentsRetrieveRequest,
+    _operation: Op,
+    _storage_scheme: enums::MerchantStorageScheme,
+) -> RouterResult<operations::GetTrackerResponse<'a, F, api::PaymentsRetrieveRequest, PaymentData<F>>>
+{
+    todo!()
+}
+
+#[cfg(all(
+    any(feature = "v2", feature = "v1"),
+    not(feature = "payment_methods_v2")
+))]
 async fn get_tracker_for_sync<
     'a,
     F: Send + Clone,
diff --git a/crates/router/src/core/pm_auth.rs b/crates/router/src/core/pm_auth.rs
index f948d7faaf9..2a93f8c215d 100644
--- a/crates/router/src/core/pm_auth.rs
+++ b/crates/router/src/core/pm_auth.rs
@@ -489,7 +489,18 @@ async fn store_bank_details_in_payment_methods(
                     .await
                     .change_context(ApiErrorResponse::InternalServerError)
                     .attach_printable("Unable to encrypt customer details")?;
+
+            #[cfg(all(
+                any(feature = "v1", feature = "v2"),
+                not(feature = "payment_methods_v2")
+            ))]
             let pm_id = generate_id(consts::ID_LENGTH, "pm");
+
+            #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+            let pm_id = common_utils::id_type::GlobalPaymentMethodId::generate("random_cell_id")
+                .change_context(errors::ApiErrorResponse::InternalServerError)
+                .attach_printable("Unable to generate GlobalPaymentMethodId")?;
+
             let now = common_utils::date_time::now();
             #[cfg(all(
                 any(feature = "v1", feature = "v2"),
diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs
index 4dc77211857..8120ccc8089 100644
--- a/crates/router/src/db/kafka_store.rs
+++ b/crates/router/src/db/kafka_store.rs
@@ -1821,6 +1821,10 @@ impl PaymentIntentInterface for KafkaStore {
 
 #[async_trait::async_trait]
 impl PaymentMethodInterface for KafkaStore {
+    #[cfg(all(
+        any(feature = "v2", feature = "v1"),
+        not(feature = "payment_methods_v2")
+    ))]
     async fn find_payment_method(
         &self,
         state: &KeyManagerState,
@@ -1833,6 +1837,19 @@ impl PaymentMethodInterface for KafkaStore {
             .await
     }
 
+    #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+    async fn find_payment_method(
+        &self,
+        state: &KeyManagerState,
+        key_store: &domain::MerchantKeyStore,
+        payment_method_id: &id_type::GlobalPaymentMethodId,
+        storage_scheme: MerchantStorageScheme,
+    ) -> CustomResult<domain::PaymentMethod, errors::StorageError> {
+        self.diesel_store
+            .find_payment_method(state, key_store, payment_method_id, storage_scheme)
+            .await
+    }
+
     #[cfg(all(
         any(feature = "v1", feature = "v2"),
         not(feature = "payment_methods_v2")
diff --git a/crates/router/src/db/payment_method.rs b/crates/router/src/db/payment_method.rs
index cf263def41c..6e052076799 100644
--- a/crates/router/src/db/payment_method.rs
+++ b/crates/router/src/db/payment_method.rs
@@ -14,6 +14,10 @@ use crate::{
 
 #[async_trait::async_trait]
 pub trait PaymentMethodInterface {
+    #[cfg(all(
+        any(feature = "v1", feature = "v2"),
+        not(feature = "payment_methods_v2")
+    ))]
     async fn find_payment_method(
         &self,
         state: &KeyManagerState,
@@ -22,6 +26,15 @@ pub trait PaymentMethodInterface {
         storage_scheme: MerchantStorageScheme,
     ) -> CustomResult<domain::PaymentMethod, errors::StorageError>;
 
+    #[cfg(all(feature = "v2", feature = "customer_v2"))]
+    async fn find_payment_method(
+        &self,
+        state: &KeyManagerState,
+        key_store: &domain::MerchantKeyStore,
+        payment_method_id: &id_type::GlobalPaymentMethodId,
+        storage_scheme: MerchantStorageScheme,
+    ) -> CustomResult<domain::PaymentMethod, errors::StorageError>;
+
     #[cfg(all(
         any(feature = "v1", feature = "v2"),
         not(feature = "payment_methods_v2")
@@ -231,7 +244,7 @@ mod storage {
             &self,
             state: &KeyManagerState,
             key_store: &domain::MerchantKeyStore,
-            payment_method_id: &str,
+            payment_method_id: &id_type::GlobalPaymentMethodId,
             storage_scheme: MerchantStorageScheme,
         ) -> CustomResult<domain::PaymentMethod, errors::StorageError> {
             let conn = connection::pg_connection_read(self).await?;
@@ -251,7 +264,8 @@ mod storage {
                 match storage_scheme {
                     MerchantStorageScheme::PostgresOnly => database_call().await,
                     MerchantStorageScheme::RedisKv => {
-                        let lookup_id = format!("payment_method_{}", payment_method_id);
+                        let lookup_id =
+                            format!("payment_method_{}", payment_method_id.get_string_repr());
                         let lookup = fallback_reverse_lookup_not_found!(
                             self.get_lookup_by_lookup_id(&lookup_id, storage_scheme)
                                 .await,
@@ -385,6 +399,38 @@ mod storage {
             .map_err(|error| report!(errors::StorageError::from(error)))
         }
 
+        #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+        #[instrument(skip_all)]
+        async fn insert_payment_method(
+            &self,
+            state: &KeyManagerState,
+            key_store: &domain::MerchantKeyStore,
+            payment_method: domain::PaymentMethod,
+            storage_scheme: MerchantStorageScheme,
+        ) -> CustomResult<domain::PaymentMethod, errors::StorageError> {
+            let payment_method_new = payment_method
+                .construct_new()
+                .await
+                .change_context(errors::StorageError::DecryptionError)?;
+
+            let conn = connection::pg_connection_write(self).await?;
+            payment_method_new
+                .insert(&conn)
+                .await
+                .map_err(|error| report!(errors::StorageError::from(error)))?
+                .convert(
+                    state,
+                    key_store.key.get_inner(),
+                    key_store.merchant_id.clone().into(),
+                )
+                .await
+                .change_context(errors::StorageError::DecryptionError)
+        }
+
+        #[cfg(all(
+            any(feature = "v1", feature = "v2"),
+            not(feature = "payment_methods_v2")
+        ))]
         #[instrument(skip_all)]
         async fn insert_payment_method(
             &self,
@@ -591,77 +637,18 @@ mod storage {
                 .await
                 .change_context(errors::StorageError::DecryptionError)?;
 
-            let merchant_id = payment_method.merchant_id.clone();
-            let customer_id = payment_method.customer_id.clone();
-            let key = PartitionKey::MerchantIdCustomerId {
-                merchant_id: &merchant_id,
-                customer_id: &customer_id,
-            };
-            let field = format!("payment_method_id_{}", payment_method.get_id());
-            let storage_scheme =
-                Box::pin(decide_storage_scheme::<_, storage_types::PaymentMethod>(
-                    self,
-                    storage_scheme,
-                    Op::Update(key.clone(), &field, payment_method.updated_by.as_deref()),
-                ))
-                .await;
-            let pm = match storage_scheme {
-                MerchantStorageScheme::PostgresOnly => {
-                    let conn = connection::pg_connection_write(self).await?;
-                    payment_method
-                        .update_with_id(
-                            &conn,
-                            payment_method_update.convert_to_payment_method_update(storage_scheme),
-                        )
-                        .await
-                        .map_err(|error| report!(errors::StorageError::from(error)))
-                }
-                MerchantStorageScheme::RedisKv => {
-                    let key_str = key.to_string();
-
-                    let p_update: PaymentMethodUpdateInternal =
-                        payment_method_update.convert_to_payment_method_update(storage_scheme);
-                    let updated_payment_method =
-                        p_update.clone().apply_changeset(payment_method.clone());
-
-                    let redis_value = serde_json::to_string(&updated_payment_method)
-                        .change_context(errors::StorageError::SerializationFailed)?;
-
-                    let redis_entry = kv::TypedSql {
-                        op: kv::DBOperation::Update {
-                            updatable: kv::Updateable::PaymentMethodUpdate(
-                                kv::PaymentMethodUpdateMems {
-                                    orig: payment_method,
-                                    update_data: p_update,
-                                },
-                            ),
-                        },
-                    };
-
-                    kv_wrapper::<(), _, _>(
-                        self,
-                        KvOperation::<diesel_models::PaymentMethod>::Hset(
-                            (&field, redis_value),
-                            redis_entry,
-                        ),
-                        key,
-                    )
-                    .await
-                    .map_err(|err| err.to_redis_failed_response(&key_str))?
-                    .try_into_hset()
-                    .change_context(errors::StorageError::KVError)?;
-
-                    Ok(updated_payment_method)
-                }
-            }?;
-
-            pm.convert(
-                state,
-                key_store.key.get_inner(),
-                key_store.merchant_id.clone().into(),
-            )
-            .await
-            .change_context(errors::StorageError::DecryptionError)
+            let conn = connection::pg_connection_write(self).await?;
+            payment_method
+                .update_with_id(&conn, payment_method_update.into())
+                .await
+                .map_err(|error| report!(errors::StorageError::from(error)))?
+                .convert(
+                    state,
+                    key_store.key.get_inner(),
+                    key_store.merchant_id.clone().into(),
+                )
+                .await
+                .change_context(errors::StorageError::DecryptionError)
         }
 
         #[cfg(all(
@@ -927,7 +914,7 @@ mod storage {
             &self,
             state: &KeyManagerState,
             key_store: &domain::MerchantKeyStore,
-            payment_method_id: &str,
+            payment_method_id: &id_type::GlobalPaymentMethodId,
             _storage_scheme: MerchantStorageScheme,
         ) -> CustomResult<domain::PaymentMethod, errors::StorageError> {
             let conn = connection::pg_connection_read(self).await?;
@@ -1258,6 +1245,10 @@ mod storage {
 
 #[async_trait::async_trait]
 impl PaymentMethodInterface for MockDb {
+    #[cfg(all(
+        any(feature = "v1", feature = "v2"),
+        not(feature = "payment_methods_v2")
+    ))]
     async fn find_payment_method(
         &self,
         state: &KeyManagerState,
@@ -1287,6 +1278,36 @@ impl PaymentMethodInterface for MockDb {
         }
     }
 
+    #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+    async fn find_payment_method(
+        &self,
+        state: &KeyManagerState,
+        key_store: &domain::MerchantKeyStore,
+        payment_method_id: &id_type::GlobalPaymentMethodId,
+        _storage_scheme: MerchantStorageScheme,
+    ) -> CustomResult<domain::PaymentMethod, errors::StorageError> {
+        let payment_methods = self.payment_methods.lock().await;
+        let payment_method = payment_methods
+            .iter()
+            .find(|pm| pm.get_id() == payment_method_id)
+            .cloned();
+
+        match payment_method {
+            Some(pm) => Ok(pm
+                .convert(
+                    state,
+                    key_store.key.get_inner(),
+                    key_store.merchant_id.clone().into(),
+                )
+                .await
+                .change_context(errors::StorageError::DecryptionError)?),
+            None => Err(errors::StorageError::ValueNotFound(
+                "cannot find payment method".to_string(),
+            )
+            .into()),
+        }
+    }
+
     #[cfg(all(
         any(feature = "v1", feature = "v2"),
         not(feature = "payment_methods_v2")
diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs
index 2bafe504dd9..aad5acbbade 100644
--- a/crates/router/src/routes/app.rs
+++ b/crates/router/src/routes/app.rs
@@ -1064,6 +1064,24 @@ impl Payouts {
     }
 }
 
+#[cfg(all(feature = "oltp", feature = "v2", feature = "payment_methods_v2",))]
+impl PaymentMethods {
+    pub fn server(state: AppState) -> Scope {
+        let mut route = web::scope("/v2/payment_methods").app_data(web::Data::new(state));
+        route = route
+            .service(web::resource("").route(web::post().to(create_payment_method_api)))
+            .service(
+                web::resource("/create-intent")
+                    .route(web::post().to(create_payment_method_intent_api)),
+            )
+            .service(
+                web::resource("/{id}/confirm-intent")
+                    .route(web::post().to(confirm_payment_method_intent_api)),
+            );
+
+        route
+    }
+}
 pub struct PaymentMethods;
 
 #[cfg(all(
diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs
index e2cb50b0aa7..725d6af81af 100644
--- a/crates/router/src/routes/payment_methods.rs
+++ b/crates/router/src/routes/payment_methods.rs
@@ -12,6 +12,11 @@ use hyperswitch_domain_models::merchant_key_store::MerchantKeyStore;
 use router_env::{instrument, logger, tracing, Flow};
 
 use super::app::{AppState, SessionState};
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+use crate::core::payment_methods::{
+    create_payment_method, list_customer_payment_method_util, payment_method_intent_confirm,
+    payment_method_intent_create,
+};
 use crate::{
     core::{
         api_locking, errors,
@@ -35,6 +40,10 @@ use crate::{
     types::api::customers::CustomerRequest,
 };
 
+#[cfg(all(
+    any(feature = "v1", feature = "v2"),
+    not(feature = "payment_methods_v2")
+))]
 #[instrument(skip_all, fields(flow = ?Flow::PaymentMethodsCreate))]
 pub async fn create_payment_method_api(
     state: web::Data<AppState>,
@@ -63,6 +72,114 @@ pub async fn create_payment_method_api(
     .await
 }
 
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[instrument(skip_all, fields(flow = ?Flow::PaymentMethodsCreate))]
+pub async fn create_payment_method_api(
+    state: web::Data<AppState>,
+    req: HttpRequest,
+    json_payload: web::Json<payment_methods::PaymentMethodCreate>,
+) -> HttpResponse {
+    let flow = Flow::PaymentMethodsCreate;
+
+    Box::pin(api::server_wrap(
+        flow,
+        state,
+        &req,
+        json_payload.into_inner(),
+        |state, auth, req, _| async move {
+            Box::pin(create_payment_method(
+                &state,
+                req,
+                &auth.merchant_account,
+                &auth.key_store,
+            ))
+            .await
+        },
+        &auth::HeaderAuth(auth::ApiKeyAuth),
+        api_locking::LockAction::NotApplicable,
+    ))
+    .await
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[instrument(skip_all, fields(flow = ?Flow::PaymentMethodsCreate))]
+pub async fn create_payment_method_intent_api(
+    state: web::Data<AppState>,
+    req: HttpRequest,
+    json_payload: web::Json<payment_methods::PaymentMethodIntentCreate>,
+) -> HttpResponse {
+    let flow = Flow::PaymentMethodsCreate;
+
+    Box::pin(api::server_wrap(
+        flow,
+        state,
+        &req,
+        json_payload.into_inner(),
+        |state, auth, req, _| async move {
+            Box::pin(payment_method_intent_create(
+                &state,
+                req,
+                &auth.merchant_account,
+                &auth.key_store,
+            ))
+            .await
+        },
+        &auth::HeaderAuth(auth::ApiKeyAuth),
+        api_locking::LockAction::NotApplicable,
+    ))
+    .await
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[instrument(skip_all, fields(flow = ?Flow::PaymentMethodsCreate))]
+pub async fn confirm_payment_method_intent_api(
+    state: web::Data<AppState>,
+    req: HttpRequest,
+    json_payload: web::Json<payment_methods::PaymentMethodIntentConfirm>,
+    path: web::Path<String>,
+) -> HttpResponse {
+    let flow = Flow::PaymentMethodsCreate;
+    let pm_id = path.into_inner();
+    let payload = json_payload.into_inner();
+
+    let (auth, _) = match auth::check_client_secret_and_get_auth(req.headers(), &payload) {
+        Ok((auth, _auth_flow)) => (auth, _auth_flow),
+        Err(e) => return api::log_and_return_error_response(e),
+    };
+
+    let inner_payload = payment_methods::PaymentMethodIntentConfirmInternal {
+        id: pm_id.clone(),
+        payment_method: payload.payment_method,
+        payment_method_type: payload.payment_method_type,
+        client_secret: payload.client_secret.clone(),
+        customer_id: payload.customer_id.to_owned(),
+        payment_method_data: payload.payment_method_data.clone(),
+    };
+
+    Box::pin(api::server_wrap(
+        flow,
+        state,
+        &req,
+        inner_payload,
+        |state, auth, req, _| {
+            let pm_id = pm_id.clone();
+            async move {
+                Box::pin(payment_method_intent_confirm(
+                    &state,
+                    req.into(),
+                    &auth.merchant_account,
+                    &auth.key_store,
+                    pm_id,
+                ))
+                .await
+            }
+        },
+        &*auth,
+        api_locking::LockAction::NotApplicable,
+    ))
+    .await
+}
+
 #[instrument(skip_all, fields(flow = ?Flow::PaymentMethodsMigrate))]
 pub async fn migrate_payment_method_api(
     state: web::Data<AppState>,
@@ -346,7 +463,7 @@ pub async fn list_customer_payment_method_for_payment(
         &req,
         payload,
         |state, auth, req, _| {
-            cards::list_customer_payment_method_util(
+            list_customer_payment_method_util(
                 state,
                 auth.merchant_account,
                 auth.key_store,
@@ -411,7 +528,7 @@ pub async fn list_customer_payment_method_api(
         &req,
         payload,
         |state, auth, req, _| {
-            cards::list_customer_payment_method_util(
+            list_customer_payment_method_util(
                 state,
                 auth.merchant_account,
                 auth.key_store,
diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs
index 16a8ed1ccc4..bcc2046e24e 100644
--- a/crates/router/src/types.rs
+++ b/crates/router/src/types.rs
@@ -11,6 +11,7 @@ pub mod authentication;
 pub mod domain;
 #[cfg(feature = "frm")]
 pub mod fraud_check;
+pub mod payment_methods;
 pub mod pm_auth;
 use masking::Secret;
 pub mod storage;
diff --git a/crates/router/src/types/api/mandates.rs b/crates/router/src/types/api/mandates.rs
index 9262877a385..b3bcfa2a887 100644
--- a/crates/router/src/types/api/mandates.rs
+++ b/crates/router/src/types/api/mandates.rs
@@ -125,75 +125,7 @@ impl MandateResponseExt for MandateResponse {
         mandate: storage::Mandate,
         storage_scheme: storage_enums::MerchantStorageScheme,
     ) -> RouterResult<Self> {
-        let db = &*state.store;
-        let payment_method = db
-            .find_payment_method(
-                &(state.into()),
-                &key_store,
-                &mandate.payment_method_id,
-                storage_scheme,
-            )
-            .await
-            .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;
-
-        let pm = payment_method
-            .payment_method
-            .get_required_value("payment_method")
-            .change_context(errors::ApiErrorResponse::PaymentMethodNotFound)
-            .attach_printable("payment_method not found")?;
-
-        let card = if pm == storage_enums::PaymentMethod::Card {
-            // if locker is disabled , decrypt the payment method data
-            let card_details = if state.conf.locker.locker_enabled {
-                let card = payment_methods::cards::get_card_from_locker(
-                    state,
-                    &payment_method.customer_id,
-                    &payment_method.merchant_id,
-                    payment_method
-                        .locker_id
-                        .as_ref()
-                        .unwrap_or(&payment_method.id),
-                )
-                .await?;
-
-                payment_methods::transformers::get_card_detail(&payment_method, card)
-                    .change_context(errors::ApiErrorResponse::InternalServerError)
-                    .attach_printable("Failed while getting card details")?
-            } else {
-                payment_methods::cards::get_card_details_without_locker_fallback(
-                    &payment_method,
-                    state,
-                )
-                .await?
-            };
-
-            Some(MandateCardDetails::from(card_details).into_inner())
-        } else {
-            None
-        };
-        let payment_method_type = payment_method
-            .payment_method_type
-            .map(|pmt| pmt.to_string());
-        Ok(Self {
-            mandate_id: mandate.mandate_id,
-            customer_acceptance: Some(api::payments::CustomerAcceptance {
-                acceptance_type: if mandate.customer_ip_address.is_some() {
-                    api::payments::AcceptanceType::Online
-                } else {
-                    api::payments::AcceptanceType::Offline
-                },
-                accepted_at: mandate.customer_accepted_at,
-                online: Some(api::payments::OnlineMandate {
-                    ip_address: mandate.customer_ip_address,
-                    user_agent: mandate.customer_user_agent.unwrap_or_default(),
-                }),
-            }),
-            card,
-            status: mandate.mandate_status,
-            payment_method: pm.to_string(),
-            payment_method_type,
-            payment_method_id: mandate.payment_method_id,
-        })
+        todo!()
     }
 }
 
diff --git a/crates/router/src/types/api/payment_methods.rs b/crates/router/src/types/api/payment_methods.rs
index f4d1492fb44..65111815b5c 100644
--- a/crates/router/src/types/api/payment_methods.rs
+++ b/crates/router/src/types/api/payment_methods.rs
@@ -5,8 +5,8 @@ pub use api_models::payment_methods::{
     GetTokenizePayloadRequest, GetTokenizePayloadResponse, ListCountriesCurrenciesRequest,
     PaymentMethodCollectLinkRenderRequest, PaymentMethodCollectLinkRequest, PaymentMethodCreate,
     PaymentMethodCreateData, PaymentMethodDeleteResponse, PaymentMethodId,
-    PaymentMethodIntentConfirm, PaymentMethodIntentCreate, PaymentMethodList,
-    PaymentMethodListData, PaymentMethodListRequest, PaymentMethodListResponse,
+    PaymentMethodIntentConfirm, PaymentMethodIntentConfirmInternal, PaymentMethodIntentCreate,
+    PaymentMethodList, PaymentMethodListData, PaymentMethodListRequest, PaymentMethodListResponse,
     PaymentMethodMigrate, PaymentMethodResponse, PaymentMethodResponseData, PaymentMethodUpdate,
     PaymentMethodsData, TokenizePayloadEncrypted, TokenizePayloadRequest, TokenizedCardValue1,
     TokenizedCardValue2, TokenizedWalletValue1, TokenizedWalletValue2,
@@ -32,6 +32,8 @@ use crate::core::{
     errors::{self, RouterResult},
     payments::helpers::validate_payment_method_type_against_payment_method,
 };
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+use crate::utils;
 
 pub(crate) trait PaymentMethodCreateExt {
     fn validate(&self) -> RouterResult<()>;
@@ -61,15 +63,63 @@ impl PaymentMethodCreateExt for PaymentMethodCreate {
 #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
 impl PaymentMethodCreateExt for PaymentMethodCreate {
     fn validate(&self) -> RouterResult<()> {
-        if !validate_payment_method_type_against_payment_method(
-            self.payment_method,
-            self.payment_method_type,
-        ) {
-            return Err(report!(errors::ApiErrorResponse::InvalidRequestData {
-                message: "Invalid 'payment_method_type' provided".to_string()
-            })
-            .attach_printable("Invalid payment method type"));
-        }
+        utils::when(
+            !validate_payment_method_type_against_payment_method(
+                self.payment_method,
+                self.payment_method_type,
+            ),
+            || {
+                return Err(report!(errors::ApiErrorResponse::InvalidRequestData {
+                    message: "Invalid 'payment_method_type' provided".to_string()
+                })
+                .attach_printable("Invalid payment method type"));
+            },
+        );
+
+        utils::when(
+            !PaymentMethodCreate::validate_payment_method_data_against_payment_method(
+                self.payment_method,
+                self.payment_method_data.clone(),
+            ),
+            || {
+                return Err(report!(errors::ApiErrorResponse::InvalidRequestData {
+                    message: "Invalid 'payment_method_data' provided".to_string()
+                })
+                .attach_printable("Invalid payment method data"));
+            },
+        );
+        Ok(())
+    }
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+impl PaymentMethodCreateExt for PaymentMethodIntentConfirm {
+    fn validate(&self) -> RouterResult<()> {
+        utils::when(
+            !validate_payment_method_type_against_payment_method(
+                self.payment_method,
+                self.payment_method_type,
+            ),
+            || {
+                return Err(report!(errors::ApiErrorResponse::InvalidRequestData {
+                    message: "Invalid 'payment_method_type' provided".to_string()
+                })
+                .attach_printable("Invalid payment method type"));
+            },
+        );
+
+        utils::when(
+            !PaymentMethodIntentConfirm::validate_payment_method_data_against_payment_method(
+                self.payment_method,
+                self.payment_method_data.clone(),
+            ),
+            || {
+                return Err(report!(errors::ApiErrorResponse::InvalidRequestData {
+                    message: "Invalid 'payment_method_data' provided".to_string()
+                })
+                .attach_printable("Invalid payment method data"));
+            },
+        );
         Ok(())
     }
 }
diff --git a/crates/router/src/types/payment_methods.rs b/crates/router/src/types/payment_methods.rs
new file mode 100644
index 00000000000..933b8e73da8
--- /dev/null
+++ b/crates/router/src/types/payment_methods.rs
@@ -0,0 +1,103 @@
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+use common_utils::generate_id;
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+use crate::{
+    consts,
+    types::{api, domain, storage},
+};
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[async_trait::async_trait]
+pub trait VaultingInterface {
+    fn get_vaulting_request_url() -> &'static str;
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[async_trait::async_trait]
+pub trait VaultingDataInterface {
+    fn get_vaulting_data_key(&self) -> String;
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[derive(Debug, serde::Deserialize, serde::Serialize)]
+pub struct VaultFingerprintRequest {
+    pub data: String,
+    pub key: String,
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[derive(Debug, serde::Deserialize, serde::Serialize)]
+pub struct VaultFingerprintResponse {
+    pub fingerprint_id: String,
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[derive(Debug, serde::Deserialize, serde::Serialize)]
+pub struct AddVaultRequest<D> {
+    pub entity_id: common_utils::id_type::MerchantId,
+    pub vault_id: String,
+    pub data: D,
+    pub ttl: i64,
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[derive(Debug, serde::Deserialize, serde::Serialize)]
+pub struct AddVaultResponse {
+    pub entity_id: common_utils::id_type::MerchantId,
+    pub vault_id: String,
+    pub fingerprint_id: Option<String>,
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[derive(Debug, serde::Deserialize, serde::Serialize)]
+pub struct AddVault;
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[derive(Debug, serde::Deserialize, serde::Serialize)]
+pub struct GetVaultFingerprint;
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[async_trait::async_trait]
+impl VaultingInterface for AddVault {
+    fn get_vaulting_request_url() -> &'static str {
+        consts::ADD_VAULT_REQUEST_URL
+    }
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[async_trait::async_trait]
+impl VaultingInterface for GetVaultFingerprint {
+    fn get_vaulting_request_url() -> &'static str {
+        consts::VAULT_FINGERPRINT_REQUEST_URL
+    }
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[async_trait::async_trait]
+impl VaultingDataInterface for api::PaymentMethodCreateData {
+    fn get_vaulting_data_key(&self) -> String {
+        match &self {
+            api::PaymentMethodCreateData::Card(card) => card.card_number.to_string(),
+        }
+    }
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+pub struct PaymentMethodClientSecret;
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+impl PaymentMethodClientSecret {
+    pub fn generate(payment_method_id: &common_utils::id_type::GlobalPaymentMethodId) -> String {
+        todo!()
+    }
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+pub struct SavedPMLPaymentsInfo {
+    pub payment_intent: storage::PaymentIntent,
+    pub business_profile: Option<domain::BusinessProfile>,
+    pub requires_cvv: bool,
+    pub off_session_payment_flag: bool,
+    pub is_connector_agnostic_mit_enabled: bool,
+}
diff --git a/crates/router/src/types/storage/payment_method.rs b/crates/router/src/types/storage/payment_method.rs
index 5bbfd8483cd..34c5714e126 100644
--- a/crates/router/src/types/storage/payment_method.rs
+++ b/crates/router/src/types/storage/payment_method.rs
@@ -19,6 +19,10 @@ pub enum PaymentTokenKind {
     Permanent,
 }
 
+#[cfg(all(
+    any(feature = "v1", feature = "v2"),
+    not(feature = "payment_methods_v2")
+))]
 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 pub struct CardTokenData {
     pub payment_method_id: Option<String>,
@@ -27,6 +31,14 @@ pub struct CardTokenData {
     pub network_token_locker_id: Option<String>,
 }
 
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
+pub struct CardTokenData {
+    pub payment_method_id: Option<common_utils::id_type::GlobalPaymentMethodId>,
+    pub locker_id: Option<String>,
+    pub token: String,
+}
+
 #[derive(Debug, Clone, serde::Serialize, Default, serde::Deserialize)]
 pub struct PaymentMethodDataWithId {
     pub payment_method: Option<enums::PaymentMethod>,
@@ -58,6 +70,10 @@ pub enum PaymentTokenData {
 }
 
 impl PaymentTokenData {
+    #[cfg(all(
+        any(feature = "v1", feature = "v2"),
+        not(feature = "payment_methods_v2")
+    ))]
     pub fn permanent_card(
         payment_method_id: Option<String>,
         locker_id: Option<String>,
@@ -72,6 +88,19 @@ impl PaymentTokenData {
         })
     }
 
+    #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+    pub fn permanent_card(
+        payment_method_id: Option<common_utils::id_type::GlobalPaymentMethodId>,
+        locker_id: Option<String>,
+        token: String,
+    ) -> Self {
+        Self::PermanentCard(CardTokenData {
+            payment_method_id,
+            locker_id,
+            token,
+        })
+    }
+
     pub fn temporary_generic(token: String) -> Self {
         Self::TemporaryGeneric(GenericTokenData { token })
     }
diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs
index 1442f5b8d48..a6f6d7c4c5a 100644
--- a/crates/router/src/types/transformers.rs
+++ b/crates/router/src/types/transformers.rs
@@ -129,19 +129,7 @@ impl
             domain::PaymentMethod,
         ),
     ) -> Self {
-        Self {
-            merchant_id: item.merchant_id.to_owned(),
-            customer_id: item.customer_id.to_owned(),
-            payment_method_id: item.get_id().clone(),
-            payment_method: item.payment_method,
-            payment_method_type: item.payment_method_type,
-            payment_method_data: card_details.map(payment_methods::PaymentMethodResponseData::Card),
-            recurring_enabled: false,
-            metadata: item.metadata,
-            created: Some(item.created_at),
-            last_used_at: None,
-            client_secret: item.client_secret,
-        }
+        todo!()
     }
 }
 
diff --git a/crates/router/src/workflows/payment_method_status_update.rs b/crates/router/src/workflows/payment_method_status_update.rs
index abc30074cda..124417e3355 100644
--- a/crates/router/src/workflows/payment_method_status_update.rs
+++ b/crates/router/src/workflows/payment_method_status_update.rs
@@ -14,6 +14,10 @@ pub struct PaymentMethodStatusUpdateWorkflow;
 
 #[async_trait::async_trait]
 impl ProcessTrackerWorkflow<SessionState> for PaymentMethodStatusUpdateWorkflow {
+    #[cfg(all(
+        any(feature = "v2", feature = "v1"),
+        not(feature = "payment_methods_v2")
+    ))]
     async fn execute_workflow<'a>(
         &'a self,
         state: &'a SessionState,
diff --git a/crates/storage_impl/src/lib.rs b/crates/storage_impl/src/lib.rs
index 6a796386777..cadb78944ee 100644
--- a/crates/storage_impl/src/lib.rs
+++ b/crates/storage_impl/src/lib.rs
@@ -437,7 +437,7 @@ impl UniqueConstraints for diesel_models::PaymentMethod {
 #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
 impl UniqueConstraints for diesel_models::PaymentMethod {
     fn unique_constraints(&self) -> Vec<String> {
-        vec![format!("paymentmethod_{}", self.id)]
+        vec![self.id.get_string_repr()]
     }
     fn table_name(&self) -> &str {
         "PaymentMethod"

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

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

## Description
<!-- Describe your changes in detail -->
- Core logic for Payment method create API for v2
- Does not contain global_id (will raise separate PR for that)

### Additional Changes

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

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

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


## Motivation and Context
<!--
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)?
-->
Vault dev is incomplete as of now so cannot test.


## Checklist
<!-- Put an `x` in the boxes that apply -->

- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [ ] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
