Bug: Add a column (connector_wallets_details) in merchant connector account and develop an api  (migrate cert) to store the encrypted certificates in the added column.



Currently apple pay related details are being stored in the connector metadata, as in future we are going to add apple pay decrypted flow support for ios app we will be collecting the apple pay payment processing certificates as well. As this ppc and ppc key needs to be securely stored, we need to move the existing certificates to a new column which stores the encrypted certificates.
diff --git a/crates/api_models/src/apple_pay_certificates_migration.rs b/crates/api_models/src/apple_pay_certificates_migration.rs
new file mode 100644
index 00000000000..796734f53e4
--- /dev/null
+++ b/crates/api_models/src/apple_pay_certificates_migration.rs
@@ -0,0 +1,12 @@
+#[derive(Debug, Clone, serde::Serialize)]
+pub struct ApplePayCertificatesMigrationResponse {
+    pub migration_successful: Vec<String>,
+    pub migration_failed: Vec<String>,
+}
+
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
+pub struct ApplePayCertificatesMigrationRequest {
+    pub merchant_ids: Vec<String>,
+}
+
+impl common_utils::events::ApiEventMetric for ApplePayCertificatesMigrationRequest {}
diff --git a/crates/api_models/src/events.rs b/crates/api_models/src/events.rs
index 9c26576e77b..078c27e6db9 100644
--- a/crates/api_models/src/events.rs
+++ b/crates/api_models/src/events.rs
@@ -1,3 +1,4 @@
+pub mod apple_pay_certificates_migration;
 pub mod connector_onboarding;
 pub mod customer;
 pub mod dispute;
diff --git a/crates/api_models/src/events/apple_pay_certificates_migration.rs b/crates/api_models/src/events/apple_pay_certificates_migration.rs
new file mode 100644
index 00000000000..f194443bea0
--- /dev/null
+++ b/crates/api_models/src/events/apple_pay_certificates_migration.rs
@@ -0,0 +1,9 @@
+use common_utils::events::ApiEventMetric;
+
+use crate::apple_pay_certificates_migration::ApplePayCertificatesMigrationResponse;
+
+impl ApiEventMetric for ApplePayCertificatesMigrationResponse {
+    fn get_api_event_type(&self) -> Option<common_utils::events::ApiEventsType> {
+        Some(common_utils::events::ApiEventsType::ApplePayCertificatesMigration)
+    }
+}
diff --git a/crates/api_models/src/lib.rs b/crates/api_models/src/lib.rs
index a0bc6f6362d..d6d6deaa235 100644
--- a/crates/api_models/src/lib.rs
+++ b/crates/api_models/src/lib.rs
@@ -2,6 +2,7 @@
 pub mod admin;
 pub mod analytics;
 pub mod api_keys;
+pub mod apple_pay_certificates_migration;
 pub mod blocklist;
 pub mod cards_info;
 pub mod conditional_configs;
diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs
index cda13289aa5..8ccfa14eaf6 100644
--- a/crates/api_models/src/payments.rs
+++ b/crates/api_models/src/payments.rs
@@ -4208,6 +4208,23 @@ pub struct SessionTokenInfo {
     pub initiative_context: String,
     #[schema(value_type = Option<CountryAlpha2>)]
     pub merchant_business_country: Option<api_enums::CountryAlpha2>,
+    #[serde(flatten)]
+    pub payment_processing_details_at: Option<PaymentProcessingDetailsAt>,
+}
+
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
+#[serde(tag = "payment_processing_details_at")]
+pub enum PaymentProcessingDetailsAt {
+    Hyperswitch(PaymentProcessingDetails),
+    Connector,
+}
+
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, ToSchema)]
+pub struct PaymentProcessingDetails {
+    #[schema(value_type = String)]
+    pub payment_processing_certificate: Secret<String>,
+    #[schema(value_type = String)]
+    pub payment_processing_certificate_key: Secret<String>,
 }
 
 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs
index 3df291d5681..f20d2b821dd 100644
--- a/crates/common_enums/src/enums.rs
+++ b/crates/common_enums/src/enums.rs
@@ -2284,11 +2284,6 @@ pub enum ReconStatus {
     Active,
     Disabled,
 }
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum ApplePayFlow {
-    Simplified,
-    Manual,
-}
 
 #[derive(
     Clone,
diff --git a/crates/common_utils/src/events.rs b/crates/common_utils/src/events.rs
index 8939e07a76c..1052840dbc8 100644
--- a/crates/common_utils/src/events.rs
+++ b/crates/common_utils/src/events.rs
@@ -50,6 +50,7 @@ pub enum ApiEventsType {
     // TODO: This has to be removed once the corresponding apiEventTypes are created
     Miscellaneous,
     RustLocker,
+    ApplePayCertificatesMigration,
     FraudCheck,
     Recon,
     Dispute {
diff --git a/crates/connector_configs/src/connector.rs b/crates/connector_configs/src/connector.rs
index d7c505f7942..b411b1a7acd 100644
--- a/crates/connector_configs/src/connector.rs
+++ b/crates/connector_configs/src/connector.rs
@@ -67,7 +67,7 @@ pub enum ConnectorAuthType {
 #[derive(Debug, Deserialize, serde::Serialize, Clone)]
 #[serde(untagged)]
 pub enum ApplePayTomlConfig {
-    Standard(payments::ApplePayMetadata),
+    Standard(Box<payments::ApplePayMetadata>),
     Zen(ZenApplePay),
 }
 
diff --git a/crates/diesel_models/src/merchant_connector_account.rs b/crates/diesel_models/src/merchant_connector_account.rs
index e45ef002626..680e3dacc85 100644
--- a/crates/diesel_models/src/merchant_connector_account.rs
+++ b/crates/diesel_models/src/merchant_connector_account.rs
@@ -43,6 +43,7 @@ pub struct MerchantConnectorAccount {
     pub applepay_verified_domains: Option<Vec<String>>,
     pub pm_auth_config: Option<serde_json::Value>,
     pub status: storage_enums::ConnectorStatus,
+    pub connector_wallets_details: Option<Encryption>,
 }
 
 #[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)]
@@ -72,6 +73,7 @@ pub struct MerchantConnectorAccountNew {
     pub applepay_verified_domains: Option<Vec<String>>,
     pub pm_auth_config: Option<serde_json::Value>,
     pub status: storage_enums::ConnectorStatus,
+    pub connector_wallets_details: Option<Encryption>,
 }
 
 #[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)]
@@ -96,6 +98,7 @@ pub struct MerchantConnectorAccountUpdateInternal {
     pub applepay_verified_domains: Option<Vec<String>>,
     pub pm_auth_config: Option<serde_json::Value>,
     pub status: Option<storage_enums::ConnectorStatus>,
+    pub connector_wallets_details: Option<Encryption>,
 }
 
 impl MerchantConnectorAccountUpdateInternal {
diff --git a/crates/diesel_models/src/query/generics.rs b/crates/diesel_models/src/query/generics.rs
index 0527ff3a181..682766679fd 100644
--- a/crates/diesel_models/src/query/generics.rs
+++ b/crates/diesel_models/src/query/generics.rs
@@ -166,7 +166,8 @@ where
         }
         Err(DieselError::NotFound) => Err(report!(errors::DatabaseError::NotFound))
             .attach_printable_lazy(|| format!("Error while updating {debug_values}")),
-        _ => Err(report!(errors::DatabaseError::Others))
+        Err(error) => Err(error)
+            .change_context(errors::DatabaseError::Others)
             .attach_printable_lazy(|| format!("Error while updating {debug_values}")),
     }
 }
@@ -252,7 +253,8 @@ where
         }
         Err(DieselError::NotFound) => Err(report!(errors::DatabaseError::NotFound))
             .attach_printable_lazy(|| format!("Error while updating by ID {debug_values}")),
-        _ => Err(report!(errors::DatabaseError::Others))
+        Err(error) => Err(error)
+            .change_context(errors::DatabaseError::Others)
             .attach_printable_lazy(|| format!("Error while updating by ID {debug_values}")),
     }
 }
diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs
index 6074fdc10b7..7baaa337259 100644
--- a/crates/diesel_models/src/schema.rs
+++ b/crates/diesel_models/src/schema.rs
@@ -679,6 +679,7 @@ diesel::table! {
         applepay_verified_domains -> Nullable<Array<Nullable<Text>>>,
         pm_auth_config -> Nullable<Jsonb>,
         status -> ConnectorStatus,
+        connector_wallets_details -> Nullable<Bytea>,
     }
 }
 
diff --git a/crates/hyperswitch_domain_models/src/payment_method_data.rs b/crates/hyperswitch_domain_models/src/payment_method_data.rs
index 065290b6b2d..8dca0c86a2a 100644
--- a/crates/hyperswitch_domain_models/src/payment_method_data.rs
+++ b/crates/hyperswitch_domain_models/src/payment_method_data.rs
@@ -22,6 +22,12 @@ pub enum PaymentMethodData {
     CardToken(CardToken),
 }
 
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum ApplePayFlow {
+    Simplified(api_models::payments::PaymentProcessingDetails),
+    Manual,
+}
+
 impl PaymentMethodData {
     pub fn get_payment_method(&self) -> Option<common_enums::PaymentMethod> {
         match self {
diff --git a/crates/hyperswitch_domain_models/src/router_data.rs b/crates/hyperswitch_domain_models/src/router_data.rs
index b5d96a5c53f..00e13f5fce9 100644
--- a/crates/hyperswitch_domain_models/src/router_data.rs
+++ b/crates/hyperswitch_domain_models/src/router_data.rs
@@ -3,7 +3,7 @@ use std::{collections::HashMap, marker::PhantomData};
 use common_utils::id_type;
 use masking::Secret;
 
-use crate::payment_address::PaymentAddress;
+use crate::{payment_address::PaymentAddress, payment_method_data};
 
 #[derive(Debug, Clone)]
 pub struct RouterData<Flow, Request, Response> {
@@ -22,6 +22,7 @@ pub struct RouterData<Flow, Request, Response> {
     pub address: PaymentAddress,
     pub auth_type: common_enums::enums::AuthenticationType,
     pub connector_meta_data: Option<common_utils::pii::SecretSerdeValue>,
+    pub connector_wallets_details: Option<common_utils::pii::SecretSerdeValue>,
     pub amount_captured: Option<i64>,
     pub access_token: Option<AccessToken>,
     pub session_token: Option<String>,
@@ -56,7 +57,7 @@ pub struct RouterData<Flow, Request, Response> {
     pub connector_http_status_code: Option<u16>,
     pub external_latency: Option<u128>,
     /// Contains apple pay flow type simplified or manual
-    pub apple_pay_flow: Option<common_enums::enums::ApplePayFlow>,
+    pub apple_pay_flow: Option<payment_method_data::ApplePayFlow>,
 
     pub frm_metadata: Option<common_utils::pii::SecretSerdeValue>,
 
diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs
index 237cddcf94e..f4fc5fc8473 100644
--- a/crates/openapi/src/openapi.rs
+++ b/crates/openapi/src/openapi.rs
@@ -309,6 +309,8 @@ Never share your secret api keys. Keep them guarded and secure.
         api_models::payments::FeatureMetadata,
         api_models::payments::ApplepayConnectorMetadataRequest,
         api_models::payments::SessionTokenInfo,
+        api_models::payments::PaymentProcessingDetailsAt,
+        api_models::payments::PaymentProcessingDetails,
         api_models::payments::SwishQrData,
         api_models::payments::AirwallexData,
         api_models::payments::NoonData,
diff --git a/crates/router/src/core.rs b/crates/router/src/core.rs
index 02a5873429f..53787fe04ab 100644
--- a/crates/router/src/core.rs
+++ b/crates/router/src/core.rs
@@ -1,6 +1,7 @@
 pub mod admin;
 pub mod api_keys;
 pub mod api_locking;
+pub mod apple_pay_certificates_migration;
 pub mod authentication;
 pub mod blocklist;
 pub mod cache;
diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs
index 6b582c2c957..0578945006f 100644
--- a/crates/router/src/core/admin.rs
+++ b/crates/router/src/core/admin.rs
@@ -937,7 +937,7 @@ pub async fn create_payment_connector(
         payment_methods_enabled,
         test_mode: req.test_mode,
         disabled,
-        metadata: req.metadata,
+        metadata: req.metadata.clone(),
         frm_configs,
         connector_label: Some(connector_label.clone()),
         business_country: req.business_country,
@@ -961,6 +961,7 @@ pub async fn create_payment_connector(
         applepay_verified_domains: None,
         pm_auth_config: req.pm_auth_config.clone(),
         status: connector_status,
+        connector_wallets_details: helpers::get_encrypted_apple_pay_connector_wallets_details(&key_store, &req.metadata).await?,
     };
 
     let transaction_type = match req.connector_type {
@@ -1200,6 +1201,7 @@ pub async fn update_payment_connector(
             expected_format: "auth_type and api_key".to_string(),
         })?;
     let metadata = req.metadata.clone().or(mca.metadata.clone());
+
     let connector_name = mca.connector_name.as_ref();
     let connector_enum = api_models::enums::Connector::from_str(connector_name)
         .change_context(errors::ApiErrorResponse::InvalidDataValue {
@@ -1275,6 +1277,10 @@ pub async fn update_payment_connector(
         applepay_verified_domains: None,
         pm_auth_config: req.pm_auth_config,
         status: Some(connector_status),
+        connector_wallets_details: helpers::get_encrypted_apple_pay_connector_wallets_details(
+            &key_store, &metadata,
+        )
+        .await?,
     };
 
     // Profile id should always be present
diff --git a/crates/router/src/core/apple_pay_certificates_migration.rs b/crates/router/src/core/apple_pay_certificates_migration.rs
new file mode 100644
index 00000000000..327358bda50
--- /dev/null
+++ b/crates/router/src/core/apple_pay_certificates_migration.rs
@@ -0,0 +1,109 @@
+use api_models::apple_pay_certificates_migration;
+use common_utils::errors::CustomResult;
+use error_stack::ResultExt;
+use masking::{PeekInterface, Secret};
+
+use super::{
+    errors::{self, StorageErrorExt},
+    payments::helpers,
+};
+use crate::{
+    routes::SessionState,
+    services::{self, logger},
+    types::{domain::types as domain_types, storage},
+};
+
+pub async fn apple_pay_certificates_migration(
+    state: SessionState,
+    req: &apple_pay_certificates_migration::ApplePayCertificatesMigrationRequest,
+) -> CustomResult<
+    services::ApplicationResponse<
+        apple_pay_certificates_migration::ApplePayCertificatesMigrationResponse,
+    >,
+    errors::ApiErrorResponse,
+> {
+    let db = state.store.as_ref();
+
+    let merchant_id_list = &req.merchant_ids;
+
+    let mut migration_successful_merchant_ids = vec![];
+    let mut migration_failed_merchant_ids = vec![];
+
+    for merchant_id in merchant_id_list {
+        let key_store = state
+            .store
+            .get_merchant_key_store_by_merchant_id(
+                merchant_id,
+                &state.store.get_master_key().to_vec().into(),
+            )
+            .await
+            .change_context(errors::ApiErrorResponse::InternalServerError)?;
+
+        let merchant_connector_accounts = db
+            .find_merchant_connector_account_by_merchant_id_and_disabled_list(
+                merchant_id,
+                true,
+                &key_store,
+            )
+            .await
+            .to_not_found_response(errors::ApiErrorResponse::InternalServerError)?;
+
+        let mut mca_to_update = vec![];
+
+        for connector_account in merchant_connector_accounts {
+            let connector_apple_pay_metadata =
+                helpers::get_applepay_metadata(connector_account.clone().metadata)
+                    .map_err(|error| {
+                        logger::error!(
+                "Apple pay metadata parsing failed for {:?} in certificates migrations api {:?}",
+                connector_account.clone().connector_name,
+                error
+            )
+                    })
+                    .ok();
+            if let Some(apple_pay_metadata) = connector_apple_pay_metadata {
+                let encrypted_apple_pay_metadata = domain_types::encrypt(
+                    Secret::new(
+                        serde_json::to_value(apple_pay_metadata)
+                            .change_context(errors::ApiErrorResponse::InternalServerError)
+                            .attach_printable("Failed to serialize apple pay metadata as JSON")?,
+                    ),
+                    key_store.key.get_inner().peek(),
+                )
+                .await
+                .change_context(errors::ApiErrorResponse::InternalServerError)
+                .attach_printable("Unable to encrypt connector apple pay metadata")?;
+
+                let updated_mca =
+                    storage::MerchantConnectorAccountUpdate::ConnectorWalletDetailsUpdate {
+                        connector_wallets_details: encrypted_apple_pay_metadata,
+                    };
+
+                mca_to_update.push((connector_account, updated_mca.into()));
+            }
+        }
+
+        let merchant_connector_accounts_update = db
+            .update_multiple_merchant_connector_accounts(mca_to_update)
+            .await;
+
+        match merchant_connector_accounts_update {
+            Ok(_) => {
+                logger::debug!("Merchant connector accounts updated for merchant id {merchant_id}");
+                migration_successful_merchant_ids.push(merchant_id.to_string());
+            }
+            Err(error) => {
+                logger::debug!(
+                    "Merchant connector accounts update failed with error {error} for merchant id {merchant_id}");
+                migration_failed_merchant_ids.push(merchant_id.to_string());
+            }
+        };
+    }
+
+    Ok(services::api::ApplicationResponse::Json(
+        apple_pay_certificates_migration::ApplePayCertificatesMigrationResponse {
+            migration_successful: migration_successful_merchant_ids,
+            migration_failed: migration_failed_merchant_ids,
+        },
+    ))
+}
diff --git a/crates/router/src/core/authentication/transformers.rs b/crates/router/src/core/authentication/transformers.rs
index 2255e1ff582..704784c485e 100644
--- a/crates/router/src/core/authentication/transformers.rs
+++ b/crates/router/src/core/authentication/transformers.rs
@@ -153,6 +153,7 @@ pub fn construct_router_data<F: Clone, Req, Res>(
         address,
         auth_type: common_enums::AuthenticationType::NoThreeDs,
         connector_meta_data: merchant_connector_account.get_metadata(),
+        connector_wallets_details: merchant_connector_account.get_connector_wallets_details(),
         amount_captured: None,
         access_token: None,
         session_token: None,
diff --git a/crates/router/src/core/fraud_check/flows/checkout_flow.rs b/crates/router/src/core/fraud_check/flows/checkout_flow.rs
index 74353e83b3d..679a5dad29a 100644
--- a/crates/router/src/core/fraud_check/flows/checkout_flow.rs
+++ b/crates/router/src/core/fraud_check/flows/checkout_flow.rs
@@ -67,6 +67,7 @@ impl ConstructFlowSpecificData<frm_api::Checkout, FraudCheckCheckoutData, FraudC
             address: self.address.clone(),
             auth_type: storage_enums::AuthenticationType::NoThreeDs,
             connector_meta_data: None,
+            connector_wallets_details: None,
             amount_captured: None,
             request: FraudCheckCheckoutData {
                 amount: self.payment_attempt.amount.get_amount_as_i64(),
diff --git a/crates/router/src/core/fraud_check/flows/fulfillment_flow.rs b/crates/router/src/core/fraud_check/flows/fulfillment_flow.rs
index f91d87c808c..855b87f3ffb 100644
--- a/crates/router/src/core/fraud_check/flows/fulfillment_flow.rs
+++ b/crates/router/src/core/fraud_check/flows/fulfillment_flow.rs
@@ -72,6 +72,7 @@ pub async fn construct_fulfillment_router_data<'a>(
         address: PaymentAddress::default(),
         auth_type: payment_attempt.authentication_type.unwrap_or_default(),
         connector_meta_data: merchant_connector_account.get_metadata(),
+        connector_wallets_details: merchant_connector_account.get_connector_wallets_details(),
         amount_captured: payment_intent
             .amount_captured
             .map(|amt| amt.get_amount_as_i64()),
diff --git a/crates/router/src/core/fraud_check/flows/record_return.rs b/crates/router/src/core/fraud_check/flows/record_return.rs
index b8f8810f09b..ac661231ecc 100644
--- a/crates/router/src/core/fraud_check/flows/record_return.rs
+++ b/crates/router/src/core/fraud_check/flows/record_return.rs
@@ -65,6 +65,7 @@ impl ConstructFlowSpecificData<RecordReturn, FraudCheckRecordReturnData, FraudCh
             address: self.address.clone(),
             auth_type: storage_enums::AuthenticationType::NoThreeDs,
             connector_meta_data: None,
+            connector_wallets_details: None,
             amount_captured: None,
             request: FraudCheckRecordReturnData {
                 amount: self.payment_attempt.amount.get_amount_as_i64(),
diff --git a/crates/router/src/core/fraud_check/flows/sale_flow.rs b/crates/router/src/core/fraud_check/flows/sale_flow.rs
index b605c540656..6dbde23e3d2 100644
--- a/crates/router/src/core/fraud_check/flows/sale_flow.rs
+++ b/crates/router/src/core/fraud_check/flows/sale_flow.rs
@@ -62,6 +62,7 @@ impl ConstructFlowSpecificData<frm_api::Sale, FraudCheckSaleData, FraudCheckResp
             address: self.address.clone(),
             auth_type: storage_enums::AuthenticationType::NoThreeDs,
             connector_meta_data: None,
+            connector_wallets_details: None,
             amount_captured: None,
             request: FraudCheckSaleData {
                 amount: self.payment_attempt.amount.get_amount_as_i64(),
diff --git a/crates/router/src/core/fraud_check/flows/transaction_flow.rs b/crates/router/src/core/fraud_check/flows/transaction_flow.rs
index 8c496792a81..4f5d0b30aa9 100644
--- a/crates/router/src/core/fraud_check/flows/transaction_flow.rs
+++ b/crates/router/src/core/fraud_check/flows/transaction_flow.rs
@@ -72,6 +72,7 @@ impl
             address: self.address.clone(),
             auth_type: storage_enums::AuthenticationType::NoThreeDs,
             connector_meta_data: None,
+            connector_wallets_details: None,
             amount_captured: None,
             request: FraudCheckTransactionData {
                 amount: self.payment_attempt.amount.get_amount_as_i64(),
diff --git a/crates/router/src/core/mandate/utils.rs b/crates/router/src/core/mandate/utils.rs
index ec438ee6d78..811b69e4562 100644
--- a/crates/router/src/core/mandate/utils.rs
+++ b/crates/router/src/core/mandate/utils.rs
@@ -44,6 +44,7 @@ pub async fn construct_mandate_revoke_router_data(
         address: PaymentAddress::default(),
         auth_type: diesel_models::enums::AuthenticationType::default(),
         connector_meta_data: None,
+        connector_wallets_details: None,
         amount_captured: None,
         access_token: None,
         session_token: None,
diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs
index caaecdc8629..8ee8dce6393 100644
--- a/crates/router/src/core/payments.rs
+++ b/crates/router/src/core/payments.rs
@@ -1475,44 +1475,47 @@ where
 
     // Tokenization Action will be DecryptApplePayToken, only when payment method type is Apple Pay
     // and the connector supports Apple Pay predecrypt
-    if matches!(
-        tokenization_action,
-        TokenizationAction::DecryptApplePayToken
-            | TokenizationAction::TokenizeInConnectorAndApplepayPreDecrypt
-    ) {
-        let apple_pay_data = match payment_data.payment_method_data.clone() {
-            Some(payment_data) => {
-                let domain_data = domain::PaymentMethodData::from(payment_data);
-                match domain_data {
-                    domain::PaymentMethodData::Wallet(domain::WalletData::ApplePay(
-                        wallet_data,
-                    )) => Some(
-                        ApplePayData::token_json(domain::WalletData::ApplePay(wallet_data))
-                            .change_context(errors::ApiErrorResponse::InternalServerError)?
-                            .decrypt(state)
-                            .await
-                            .change_context(errors::ApiErrorResponse::InternalServerError)?,
-                    ),
-                    _ => None,
+    match &tokenization_action {
+        TokenizationAction::DecryptApplePayToken(payment_processing_details)
+        | TokenizationAction::TokenizeInConnectorAndApplepayPreDecrypt(
+            payment_processing_details,
+        ) => {
+            let apple_pay_data = match payment_data.payment_method_data.clone() {
+                Some(payment_method_data) => {
+                    let domain_data = domain::PaymentMethodData::from(payment_method_data);
+                    match domain_data {
+                        domain::PaymentMethodData::Wallet(domain::WalletData::ApplePay(
+                            wallet_data,
+                        )) => Some(
+                            ApplePayData::token_json(domain::WalletData::ApplePay(wallet_data))
+                                .change_context(errors::ApiErrorResponse::InternalServerError)?
+                                .decrypt(
+                                    &payment_processing_details.payment_processing_certificate,
+                                    &payment_processing_details.payment_processing_certificate_key,
+                                )
+                                .await
+                                .change_context(errors::ApiErrorResponse::InternalServerError)?,
+                        ),
+                        _ => None,
+                    }
                 }
-            }
-            _ => None,
-        };
-
-        let apple_pay_predecrypt = apple_pay_data
-            .parse_value::<hyperswitch_domain_models::router_data::ApplePayPredecryptData>(
-                "ApplePayPredecryptData",
-            )
-            .change_context(errors::ApiErrorResponse::InternalServerError)?;
+                _ => None,
+            };
 
-        logger::debug!(?apple_pay_predecrypt);
+            let apple_pay_predecrypt = apple_pay_data
+                .parse_value::<hyperswitch_domain_models::router_data::ApplePayPredecryptData>(
+                    "ApplePayPredecryptData",
+                )
+                .change_context(errors::ApiErrorResponse::InternalServerError)?;
 
-        router_data.payment_method_token = Some(
-            hyperswitch_domain_models::router_data::PaymentMethodToken::ApplePayDecrypt(Box::new(
-                apple_pay_predecrypt,
-            )),
-        );
-    }
+            router_data.payment_method_token = Some(
+                hyperswitch_domain_models::router_data::PaymentMethodToken::ApplePayDecrypt(
+                    Box::new(apple_pay_predecrypt),
+                ),
+            );
+        }
+        _ => (),
+    };
 
     let pm_token = router_data
         .add_payment_method_token(state, &connector, &tokenization_action)
@@ -2053,7 +2056,7 @@ fn is_payment_method_tokenization_enabled_for_connector(
     connector_name: &str,
     payment_method: &storage::enums::PaymentMethod,
     payment_method_type: &Option<storage::enums::PaymentMethodType>,
-    apple_pay_flow: &Option<enums::ApplePayFlow>,
+    apple_pay_flow: &Option<domain::ApplePayFlow>,
 ) -> RouterResult<bool> {
     let connector_tokenization_filter = state.conf.tokenization.0.get(connector_name);
 
@@ -2078,13 +2081,13 @@ fn is_payment_method_tokenization_enabled_for_connector(
 
 fn is_apple_pay_pre_decrypt_type_connector_tokenization(
     payment_method_type: &Option<storage::enums::PaymentMethodType>,
-    apple_pay_flow: &Option<enums::ApplePayFlow>,
+    apple_pay_flow: &Option<domain::ApplePayFlow>,
     apple_pay_pre_decrypt_flow_filter: Option<ApplePayPreDecryptFlow>,
 ) -> bool {
     match (payment_method_type, apple_pay_flow) {
         (
             Some(storage::enums::PaymentMethodType::ApplePay),
-            Some(enums::ApplePayFlow::Simplified),
+            Some(domain::ApplePayFlow::Simplified(_)),
         ) => !matches!(
             apple_pay_pre_decrypt_flow_filter,
             Some(ApplePayPreDecryptFlow::NetworkTokenization)
@@ -2094,18 +2097,22 @@ fn is_apple_pay_pre_decrypt_type_connector_tokenization(
 }
 
 fn decide_apple_pay_flow(
+    state: &SessionState,
     payment_method_type: &Option<enums::PaymentMethodType>,
     merchant_connector_account: Option<&helpers::MerchantConnectorAccountType>,
-) -> Option<enums::ApplePayFlow> {
+) -> Option<domain::ApplePayFlow> {
     payment_method_type.and_then(|pmt| match pmt {
-        enums::PaymentMethodType::ApplePay => check_apple_pay_metadata(merchant_connector_account),
+        enums::PaymentMethodType::ApplePay => {
+            check_apple_pay_metadata(state, merchant_connector_account)
+        }
         _ => None,
     })
 }
 
 fn check_apple_pay_metadata(
+    state: &SessionState,
     merchant_connector_account: Option<&helpers::MerchantConnectorAccountType>,
-) -> Option<enums::ApplePayFlow> {
+) -> Option<domain::ApplePayFlow> {
     merchant_connector_account.and_then(|mca| {
         let metadata = mca.get_metadata();
         metadata.and_then(|apple_pay_metadata| {
@@ -2139,14 +2146,34 @@ fn check_apple_pay_metadata(
                     apple_pay_combined,
                 ) => match apple_pay_combined {
                     api_models::payments::ApplePayCombinedMetadata::Simplified { .. } => {
-                        enums::ApplePayFlow::Simplified
+                        domain::ApplePayFlow::Simplified(payments_api::PaymentProcessingDetails {
+                            payment_processing_certificate: state
+                            .conf
+                            .applepay_decrypt_keys
+                            .get_inner()
+                            .apple_pay_ppc
+                            .clone(),
+                            payment_processing_certificate_key: state
+                            .conf
+                            .applepay_decrypt_keys
+                            .get_inner()
+                            .apple_pay_ppc_key
+                            .clone(),
+                         })
                     }
-                    api_models::payments::ApplePayCombinedMetadata::Manual { .. } => {
-                        enums::ApplePayFlow::Manual
+                    api_models::payments::ApplePayCombinedMetadata::Manual { payment_request_data: _, session_token_data } => {
+                        if let Some(manual_payment_processing_details_at) = session_token_data.payment_processing_details_at {
+                            match manual_payment_processing_details_at {
+                                payments_api::PaymentProcessingDetailsAt::Hyperswitch(payment_processing_details) => domain::ApplePayFlow::Simplified(payment_processing_details),
+                                payments_api::PaymentProcessingDetailsAt::Connector => domain::ApplePayFlow::Manual,
+                            }
+                        } else {
+                            domain::ApplePayFlow::Manual
+                        }
                     }
                 },
                 api_models::payments::ApplepaySessionTokenMetadata::ApplePay(_) => {
-                    enums::ApplePayFlow::Manual
+                    domain::ApplePayFlow::Manual
                 }
             })
         })
@@ -2173,23 +2200,21 @@ async fn decide_payment_method_tokenize_action(
     payment_method: &storage::enums::PaymentMethod,
     pm_parent_token: Option<&String>,
     is_connector_tokenization_enabled: bool,
-    apple_pay_flow: Option<enums::ApplePayFlow>,
+    apple_pay_flow: Option<domain::ApplePayFlow>,
 ) -> RouterResult<TokenizationAction> {
-    let is_apple_pay_predecrypt_supported =
-        matches!(apple_pay_flow, Some(enums::ApplePayFlow::Simplified));
-
     match pm_parent_token {
-        None => {
-            if is_connector_tokenization_enabled && is_apple_pay_predecrypt_supported {
-                Ok(TokenizationAction::TokenizeInConnectorAndApplepayPreDecrypt)
-            } else if is_connector_tokenization_enabled {
-                Ok(TokenizationAction::TokenizeInConnectorAndRouter)
-            } else if is_apple_pay_predecrypt_supported {
-                Ok(TokenizationAction::DecryptApplePayToken)
-            } else {
-                Ok(TokenizationAction::TokenizeInRouter)
+        None => Ok(match (is_connector_tokenization_enabled, apple_pay_flow) {
+            (true, Some(domain::ApplePayFlow::Simplified(payment_processing_details))) => {
+                TokenizationAction::TokenizeInConnectorAndApplepayPreDecrypt(
+                    payment_processing_details,
+                )
             }
-        }
+            (true, _) => TokenizationAction::TokenizeInConnectorAndRouter,
+            (false, Some(domain::ApplePayFlow::Simplified(payment_processing_details))) => {
+                TokenizationAction::DecryptApplePayToken(payment_processing_details)
+            }
+            (false, _) => TokenizationAction::TokenizeInRouter,
+        }),
         Some(token) => {
             let redis_conn = state
                 .store
@@ -2212,17 +2237,18 @@ async fn decide_payment_method_tokenize_action(
 
             match connector_token_option {
                 Some(connector_token) => Ok(TokenizationAction::ConnectorToken(connector_token)),
-                None => {
-                    if is_connector_tokenization_enabled && is_apple_pay_predecrypt_supported {
-                        Ok(TokenizationAction::TokenizeInConnectorAndApplepayPreDecrypt)
-                    } else if is_connector_tokenization_enabled {
-                        Ok(TokenizationAction::TokenizeInConnectorAndRouter)
-                    } else if is_apple_pay_predecrypt_supported {
-                        Ok(TokenizationAction::DecryptApplePayToken)
-                    } else {
-                        Ok(TokenizationAction::TokenizeInRouter)
+                None => Ok(match (is_connector_tokenization_enabled, apple_pay_flow) {
+                    (true, Some(domain::ApplePayFlow::Simplified(payment_processing_details))) => {
+                        TokenizationAction::TokenizeInConnectorAndApplepayPreDecrypt(
+                            payment_processing_details,
+                        )
                     }
-                }
+                    (true, _) => TokenizationAction::TokenizeInConnectorAndRouter,
+                    (false, Some(domain::ApplePayFlow::Simplified(payment_processing_details))) => {
+                        TokenizationAction::DecryptApplePayToken(payment_processing_details)
+                    }
+                    (false, _) => TokenizationAction::TokenizeInRouter,
+                }),
             }
         }
     }
@@ -2235,8 +2261,8 @@ pub enum TokenizationAction {
     TokenizeInConnectorAndRouter,
     ConnectorToken(String),
     SkipConnectorTokenization,
-    DecryptApplePayToken,
-    TokenizeInConnectorAndApplepayPreDecrypt,
+    DecryptApplePayToken(payments_api::PaymentProcessingDetails),
+    TokenizeInConnectorAndApplepayPreDecrypt(payments_api::PaymentProcessingDetails),
 }
 
 #[allow(clippy::too_many_arguments)]
@@ -2277,7 +2303,7 @@ where
             let payment_method_type = &payment_data.payment_attempt.payment_method_type;
 
             let apple_pay_flow =
-                decide_apple_pay_flow(payment_method_type, Some(merchant_connector_account));
+                decide_apple_pay_flow(state, payment_method_type, Some(merchant_connector_account));
 
             let is_connector_tokenization_enabled =
                 is_payment_method_tokenization_enabled_for_connector(
@@ -2346,12 +2372,14 @@ where
                 TokenizationAction::SkipConnectorTokenization => {
                     TokenizationAction::SkipConnectorTokenization
                 }
-                TokenizationAction::DecryptApplePayToken => {
-                    TokenizationAction::DecryptApplePayToken
-                }
-                TokenizationAction::TokenizeInConnectorAndApplepayPreDecrypt => {
-                    TokenizationAction::TokenizeInConnectorAndApplepayPreDecrypt
+                TokenizationAction::DecryptApplePayToken(payment_processing_details) => {
+                    TokenizationAction::DecryptApplePayToken(payment_processing_details)
                 }
+                TokenizationAction::TokenizeInConnectorAndApplepayPreDecrypt(
+                    payment_processing_details,
+                ) => TokenizationAction::TokenizeInConnectorAndApplepayPreDecrypt(
+                    payment_processing_details,
+                ),
             };
             (payment_data.to_owned(), connector_tokenization_action)
         }
diff --git a/crates/router/src/core/payments/flows/session_flow.rs b/crates/router/src/core/payments/flows/session_flow.rs
index 76441f40759..14a3b9327dc 100644
--- a/crates/router/src/core/payments/flows/session_flow.rs
+++ b/crates/router/src/core/payments/flows/session_flow.rs
@@ -166,8 +166,20 @@ async fn create_applepay_session_token(
         )
     } else {
         // Get the apple pay metadata
-        let apple_pay_metadata =
-            helpers::get_applepay_metadata(router_data.connector_meta_data.clone())?;
+        let connector_apple_pay_wallet_details =
+            helpers::get_applepay_metadata(router_data.connector_wallets_details.clone())
+                .map_err(|error| {
+                    logger::debug!(
+                        "Apple pay connector wallets details parsing failed in create_applepay_session_token {:?}",
+                        error
+                    )
+                })
+                .ok();
+
+        let apple_pay_metadata = match connector_apple_pay_wallet_details {
+            Some(apple_pay_wallet_details) => apple_pay_wallet_details,
+            None => helpers::get_applepay_metadata(router_data.connector_meta_data.clone())?,
+        };
 
         // Get payment request data , apple pay session request and merchant keys
         let (
diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs
index a58a6ae32b2..6610b048606 100644
--- a/crates/router/src/core/payments/helpers.rs
+++ b/crates/router/src/core/payments/helpers.rs
@@ -6,6 +6,7 @@ use api_models::{
 };
 use base64::Engine;
 use common_utils::{
+    crypto::Encryptable,
     ext_traits::{AsyncExt, ByteSliceExt, Encode, ValueExt},
     fp_utils, generate_id, id_type, pii,
     types::MinorUnit,
@@ -3169,6 +3170,13 @@ impl MerchantConnectorAccountType {
         }
     }
 
+    pub fn get_connector_wallets_details(&self) -> Option<masking::Secret<serde_json::Value>> {
+        match self {
+            Self::DbVal(val) => val.connector_wallets_details.as_deref().cloned(),
+            Self::CacheVal(_) => None,
+        }
+    }
+
     pub fn is_disabled(&self) -> bool {
         match self {
             Self::DbVal(ref inner) => inner.disabled.unwrap_or(false),
@@ -3368,6 +3376,7 @@ pub fn router_data_type_conversion<F1, F2, Req1, Req2, Res1, Res2>(
         refund_id: router_data.refund_id,
         dispute_id: router_data.dispute_id,
         connector_response: router_data.connector_response,
+        connector_wallets_details: router_data.connector_wallets_details,
     }
 }
 
@@ -3905,17 +3914,32 @@ pub fn validate_customer_access(
 
 pub fn is_apple_pay_simplified_flow(
     connector_metadata: Option<pii::SecretSerdeValue>,
+    connector_wallets_details: Option<pii::SecretSerdeValue>,
     connector_name: Option<&String>,
 ) -> CustomResult<bool, errors::ApiErrorResponse> {
-    let option_apple_pay_metadata = get_applepay_metadata(connector_metadata)
-        .map_err(|error| {
-            logger::info!(
-                "Apple pay metadata parsing for {:?} in is_apple_pay_simplified_flow {:?}",
+    let connector_apple_pay_wallet_details =
+        get_applepay_metadata(connector_wallets_details)
+            .map_err(|error| {
+                logger::debug!(
+                    "Apple pay connector wallets details parsing failed for {:?} in is_apple_pay_simplified_flow {:?}",
+                    connector_name,
+                    error
+                )
+            })
+            .ok();
+
+    let option_apple_pay_metadata = match connector_apple_pay_wallet_details {
+        Some(apple_pay_wallet_details) => Some(apple_pay_wallet_details),
+        None => get_applepay_metadata(connector_metadata)
+            .map_err(|error| {
+                logger::debug!(
+                "Apple pay metadata parsing failed for {:?} in is_apple_pay_simplified_flow {:?}",
                 connector_name,
                 error
             )
-        })
-        .ok();
+            })
+            .ok(),
+    };
 
     // return true only if the apple flow type is simplified
     Ok(matches!(
@@ -3928,6 +3952,38 @@ pub fn is_apple_pay_simplified_flow(
     ))
 }
 
+pub async fn get_encrypted_apple_pay_connector_wallets_details(
+    key_store: &domain::MerchantKeyStore,
+    connector_metadata: &Option<masking::Secret<tera::Value>>,
+) -> RouterResult<Option<Encryptable<masking::Secret<serde_json::Value>>>> {
+    let apple_pay_metadata = get_applepay_metadata(connector_metadata.clone())
+        .map_err(|error| {
+            logger::error!(
+                "Apple pay metadata parsing failed in get_encrypted_apple_pay_connector_wallets_details {:?}",
+                error
+            )
+        })
+        .ok();
+
+    let connector_apple_pay_details = apple_pay_metadata
+        .map(|metadata| {
+            serde_json::to_value(metadata)
+                .change_context(errors::ApiErrorResponse::InternalServerError)
+                .attach_printable("Failed to serialize apple pay metadata as JSON")
+        })
+        .transpose()?
+        .map(masking::Secret::new);
+
+    let encrypted_connector_apple_pay_details = connector_apple_pay_details
+        .async_lift(|wallets_details| {
+            types::encrypt_optional(wallets_details, key_store.key.get_inner().peek())
+        })
+        .await
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Failed while encrypting connector wallets details")?;
+    Ok(encrypted_connector_apple_pay_details)
+}
+
 pub fn get_applepay_metadata(
     connector_metadata: Option<pii::SecretSerdeValue>,
 ) -> RouterResult<api_models::payments::ApplepaySessionTokenMetadata> {
@@ -3991,6 +4047,7 @@ where
 
     let connector_data_list = if is_apple_pay_simplified_flow(
         merchant_connector_account_type.get_metadata(),
+        merchant_connector_account_type.get_connector_wallets_details(),
         merchant_connector_account_type
             .get_connector_name()
             .as_ref(),
@@ -4010,6 +4067,10 @@ where
         for merchant_connector_account in merchant_connector_account_list {
             if is_apple_pay_simplified_flow(
                 merchant_connector_account.metadata,
+                merchant_connector_account
+                    .connector_wallets_details
+                    .as_deref()
+                    .cloned(),
                 Some(&merchant_connector_account.connector_name),
             )? {
                 let connector_data = api::ConnectorData::get_connector_by_name(
@@ -4064,10 +4125,11 @@ impl ApplePayData {
 
     pub async fn decrypt(
         &self,
-        state: &SessionState,
+        payment_processing_certificate: &masking::Secret<String>,
+        payment_processing_certificate_key: &masking::Secret<String>,
     ) -> CustomResult<serde_json::Value, errors::ApplePayDecryptionError> {
-        let merchant_id = self.merchant_id(state).await?;
-        let shared_secret = self.shared_secret(state).await?;
+        let merchant_id = self.merchant_id(payment_processing_certificate)?;
+        let shared_secret = self.shared_secret(payment_processing_certificate_key)?;
         let symmetric_key = self.symmetric_key(&merchant_id, &shared_secret)?;
         let decrypted = self.decrypt_ciphertext(&symmetric_key)?;
         let parsed_decrypted: serde_json::Value = serde_json::from_str(&decrypted)
@@ -4075,17 +4137,11 @@ impl ApplePayData {
         Ok(parsed_decrypted)
     }
 
-    pub async fn merchant_id(
+    pub fn merchant_id(
         &self,
-        state: &SessionState,
+        payment_processing_certificate: &masking::Secret<String>,
     ) -> CustomResult<String, errors::ApplePayDecryptionError> {
-        let cert_data = state
-            .conf
-            .applepay_decrypt_keys
-            .get_inner()
-            .apple_pay_ppc
-            .clone()
-            .expose();
+        let cert_data = payment_processing_certificate.clone().expose();
 
         let base64_decode_cert_data = BASE64_ENGINE
             .decode(cert_data)
@@ -4120,9 +4176,9 @@ impl ApplePayData {
         Ok(apple_pay_m_id)
     }
 
-    pub async fn shared_secret(
+    pub fn shared_secret(
         &self,
-        state: &SessionState,
+        payment_processing_certificate_key: &masking::Secret<String>,
     ) -> CustomResult<Vec<u8>, errors::ApplePayDecryptionError> {
         let public_ec_bytes = BASE64_ENGINE
             .decode(self.header.ephemeral_public_key.peek().as_bytes())
@@ -4132,13 +4188,7 @@ impl ApplePayData {
             .change_context(errors::ApplePayDecryptionError::KeyDeserializationFailed)
             .attach_printable("Failed to deserialize the public key")?;
 
-        let decrypted_apple_pay_ppc_key = state
-            .conf
-            .applepay_decrypt_keys
-            .get_inner()
-            .apple_pay_ppc_key
-            .clone()
-            .expose();
+        let decrypted_apple_pay_ppc_key = payment_processing_certificate_key.clone().expose();
 
         // Create PKey objects from EcKey
         let private_key = PKey::private_key_from_pem(decrypted_apple_pay_ppc_key.as_bytes())
diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs
index 48637a0d2b3..7016c7542be 100644
--- a/crates/router/src/core/payments/tokenization.rs
+++ b/crates/router/src/core/payments/tokenization.rs
@@ -727,7 +727,7 @@ pub async fn add_payment_method_token<F: Clone, T: types::Tokenizable + Clone>(
 ) -> RouterResult<Option<String>> {
     match tokenization_action {
         payments::TokenizationAction::TokenizeInConnector
-        | payments::TokenizationAction::TokenizeInConnectorAndApplepayPreDecrypt => {
+        | payments::TokenizationAction::TokenizeInConnectorAndApplepayPreDecrypt(_) => {
             let connector_integration: services::BoxedConnectorIntegration<
                 '_,
                 api::PaymentMethodToken,
diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs
index 8eca589ca28..02115a529c3 100644
--- a/crates/router/src/core/payments/transformers.rs
+++ b/crates/router/src/core/payments/transformers.rs
@@ -125,6 +125,7 @@ where
     };
 
     let apple_pay_flow = payments::decide_apple_pay_flow(
+        state,
         &payment_data.payment_attempt.payment_method_type,
         Some(merchant_connector_account),
     );
@@ -154,6 +155,7 @@ where
             .authentication_type
             .unwrap_or_default(),
         connector_meta_data: merchant_connector_account.get_metadata(),
+        connector_wallets_details: merchant_connector_account.get_connector_wallets_details(),
         request: T::try_from(additional_data)?,
         response,
         amount_captured: payment_data
diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs
index 679838a6dd5..8201a8480c0 100644
--- a/crates/router/src/core/utils.rs
+++ b/crates/router/src/core/utils.rs
@@ -164,6 +164,7 @@ pub async fn construct_payout_router_data<'a, F>(
         address,
         auth_type: enums::AuthenticationType::default(),
         connector_meta_data: merchant_connector_account.get_metadata(),
+        connector_wallets_details: merchant_connector_account.get_connector_wallets_details(),
         amount_captured: None,
         payment_method_status: None,
         request: types::PayoutsData {
@@ -316,6 +317,7 @@ pub async fn construct_refund_router_data<'a, F>(
         address: PaymentAddress::default(),
         auth_type: payment_attempt.authentication_type.unwrap_or_default(),
         connector_meta_data: merchant_connector_account.get_metadata(),
+        connector_wallets_details: merchant_connector_account.get_connector_wallets_details(),
         amount_captured: payment_intent
             .amount_captured
             .map(|amt| amt.get_amount_as_i64()),
@@ -565,6 +567,7 @@ pub async fn construct_accept_dispute_router_data<'a>(
         address: PaymentAddress::default(),
         auth_type: payment_attempt.authentication_type.unwrap_or_default(),
         connector_meta_data: merchant_connector_account.get_metadata(),
+        connector_wallets_details: merchant_connector_account.get_connector_wallets_details(),
         amount_captured: payment_intent
             .amount_captured
             .map(|amt| amt.get_amount_as_i64()),
@@ -661,6 +664,7 @@ pub async fn construct_submit_evidence_router_data<'a>(
         address: PaymentAddress::default(),
         auth_type: payment_attempt.authentication_type.unwrap_or_default(),
         connector_meta_data: merchant_connector_account.get_metadata(),
+        connector_wallets_details: merchant_connector_account.get_connector_wallets_details(),
         amount_captured: payment_intent
             .amount_captured
             .map(|amt| amt.get_amount_as_i64()),
@@ -755,6 +759,7 @@ pub async fn construct_upload_file_router_data<'a>(
         address: PaymentAddress::default(),
         auth_type: payment_attempt.authentication_type.unwrap_or_default(),
         connector_meta_data: merchant_connector_account.get_metadata(),
+        connector_wallets_details: merchant_connector_account.get_connector_wallets_details(),
         amount_captured: payment_intent
             .amount_captured
             .map(|amt| amt.get_amount_as_i64()),
@@ -853,6 +858,7 @@ pub async fn construct_defend_dispute_router_data<'a>(
         address: PaymentAddress::default(),
         auth_type: payment_attempt.authentication_type.unwrap_or_default(),
         connector_meta_data: merchant_connector_account.get_metadata(),
+        connector_wallets_details: merchant_connector_account.get_connector_wallets_details(),
         amount_captured: payment_intent
             .amount_captured
             .map(|amt| amt.get_amount_as_i64()),
@@ -942,6 +948,7 @@ pub async fn construct_retrieve_file_router_data<'a>(
         address: PaymentAddress::default(),
         auth_type: diesel_models::enums::AuthenticationType::default(),
         connector_meta_data: merchant_connector_account.get_metadata(),
+        connector_wallets_details: merchant_connector_account.get_connector_wallets_details(),
         amount_captured: None,
         payment_method_status: None,
         request: types::RetrieveFileRequestData {
diff --git a/crates/router/src/core/verification/utils.rs b/crates/router/src/core/verification/utils.rs
index 7518091ee87..bfbc1cb8b44 100644
--- a/crates/router/src/core/verification/utils.rs
+++ b/crates/router/src/core/verification/utils.rs
@@ -61,6 +61,7 @@ pub async fn check_existence_and_add_domain_to_db(
         pm_auth_config: None,
         connector_label: None,
         status: None,
+        connector_wallets_details: None,
     };
     state
         .store
diff --git a/crates/router/src/core/webhooks/utils.rs b/crates/router/src/core/webhooks/utils.rs
index ad1aafd3125..1394c68cfa8 100644
--- a/crates/router/src/core/webhooks/utils.rs
+++ b/crates/router/src/core/webhooks/utils.rs
@@ -86,6 +86,7 @@ pub async fn construct_webhook_router_data<'a>(
         address: PaymentAddress::default(),
         auth_type: diesel_models::enums::AuthenticationType::default(),
         connector_meta_data: None,
+        connector_wallets_details: None,
         amount_captured: None,
         request: types::VerifyWebhookSourceRequestData {
             webhook_headers: request_details.headers.clone(),
diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs
index 910ad085f63..19603f81486 100644
--- a/crates/router/src/db/kafka_store.rs
+++ b/crates/router/src/db/kafka_store.rs
@@ -942,6 +942,17 @@ impl FileMetadataInterface for KafkaStore {
 
 #[async_trait::async_trait]
 impl MerchantConnectorAccountInterface for KafkaStore {
+    async fn update_multiple_merchant_connector_accounts(
+        &self,
+        merchant_connector_accounts: Vec<(
+            domain::MerchantConnectorAccount,
+            storage::MerchantConnectorAccountUpdateInternal,
+        )>,
+    ) -> CustomResult<(), errors::StorageError> {
+        self.diesel_store
+            .update_multiple_merchant_connector_accounts(merchant_connector_accounts)
+            .await
+    }
     async fn find_merchant_connector_account_by_merchant_id_connector_label(
         &self,
         merchant_id: &str,
diff --git a/crates/router/src/db/merchant_connector_account.rs b/crates/router/src/db/merchant_connector_account.rs
index aea37b517be..ab66b52c32d 100644
--- a/crates/router/src/db/merchant_connector_account.rs
+++ b/crates/router/src/db/merchant_connector_account.rs
@@ -1,4 +1,6 @@
+use async_bb8_diesel::AsyncConnection;
 use common_utils::ext_traits::{AsyncExt, ByteSliceExt, Encode};
+use diesel_models::encryption::Encryption;
 use error_stack::{report, ResultExt};
 use router_env::{instrument, tracing};
 #[cfg(feature = "accounts_cache")]
@@ -165,6 +167,14 @@ where
         key_store: &domain::MerchantKeyStore,
     ) -> CustomResult<domain::MerchantConnectorAccount, errors::StorageError>;
 
+    async fn update_multiple_merchant_connector_accounts(
+        &self,
+        this: Vec<(
+            domain::MerchantConnectorAccount,
+            storage::MerchantConnectorAccountUpdateInternal,
+        )>,
+    ) -> CustomResult<(), errors::StorageError>;
+
     async fn delete_merchant_connector_account_by_merchant_id_merchant_connector_id(
         &self,
         merchant_id: &str,
@@ -381,6 +391,104 @@ impl MerchantConnectorAccountInterface for Store {
             .await
     }
 
+    #[instrument(skip_all)]
+    async fn update_multiple_merchant_connector_accounts(
+        &self,
+        merchant_connector_accounts: Vec<(
+            domain::MerchantConnectorAccount,
+            storage::MerchantConnectorAccountUpdateInternal,
+        )>,
+    ) -> CustomResult<(), errors::StorageError> {
+        let conn = connection::pg_connection_write(self).await?;
+
+        async fn update_call(
+            connection: &diesel_models::PgPooledConn,
+            (merchant_connector_account, mca_update): (
+                domain::MerchantConnectorAccount,
+                storage::MerchantConnectorAccountUpdateInternal,
+            ),
+        ) -> Result<(), error_stack::Report<storage_impl::errors::StorageError>> {
+            Conversion::convert(merchant_connector_account)
+                .await
+                .change_context(errors::StorageError::EncryptionError)?
+                .update(connection, mca_update)
+                .await
+                .map_err(|error| report!(errors::StorageError::from(error)))?;
+            Ok(())
+        }
+
+        conn.transaction_async(|connection_pool| async move {
+            for (merchant_connector_account, update_merchant_connector_account) in
+                merchant_connector_accounts
+            {
+                let _connector_name = merchant_connector_account.connector_name.clone();
+                let _profile_id = merchant_connector_account.profile_id.clone().ok_or(
+                    errors::StorageError::ValueNotFound("profile_id".to_string()),
+                )?;
+
+                let _merchant_id = merchant_connector_account.merchant_id.clone();
+                let _merchant_connector_id =
+                    merchant_connector_account.merchant_connector_id.clone();
+
+                let update = update_call(
+                    &connection_pool,
+                    (
+                        merchant_connector_account,
+                        update_merchant_connector_account,
+                    ),
+                );
+
+                #[cfg(feature = "accounts_cache")]
+                // Redact all caches as any of might be used because of backwards compatibility
+                cache::publish_and_redact_multiple(
+                    self,
+                    [
+                        cache::CacheKind::Accounts(
+                            format!("{}_{}", _profile_id, _connector_name).into(),
+                        ),
+                        cache::CacheKind::Accounts(
+                            format!("{}_{}", _merchant_id, _merchant_connector_id).into(),
+                        ),
+                        cache::CacheKind::CGraph(
+                            format!("cgraph_{}_{_profile_id}", _merchant_id).into(),
+                        ),
+                    ],
+                    || update,
+                )
+                .await
+                .map_err(|error| {
+                    // Returning `DatabaseConnectionError` after logging the actual error because
+                    // -> it is not possible to get the underlying from `error_stack::Report<C>`
+                    // -> it is not possible to write a `From` impl to convert the `diesel::result::Error` to `error_stack::Report<StorageError>`
+                    //    because of Rust's orphan rules
+                    router_env::logger::error!(
+                        ?error,
+                        "DB transaction for updating multiple merchant connector account failed"
+                    );
+                    errors::StorageError::DatabaseConnectionError
+                })?;
+
+                #[cfg(not(feature = "accounts_cache"))]
+                {
+                    update.await.map_err(|error| {
+                        // Returning `DatabaseConnectionError` after logging the actual error because
+                        // -> it is not possible to get the underlying from `error_stack::Report<C>`
+                        // -> it is not possible to write a `From` impl to convert the `diesel::result::Error` to `error_stack::Report<StorageError>`
+                        //    because of Rust's orphan rules
+                        router_env::logger::error!(
+                        ?error,
+                        "DB transaction for updating multiple merchant connector account failed"
+                    );
+                        errors::StorageError::DatabaseConnectionError
+                    })?;
+                }
+            }
+            Ok::<_, errors::StorageError>(())
+        })
+        .await?;
+        Ok(())
+    }
+
     #[instrument(skip_all)]
     async fn update_merchant_connector_account(
         &self,
@@ -417,7 +525,7 @@ impl MerchantConnectorAccountInterface for Store {
 
         #[cfg(feature = "accounts_cache")]
         {
-            // Redact both the caches as any one or both might be used because of backwards compatibility
+            // Redact all caches as any of might be used because of backwards compatibility
             cache::publish_and_redact_multiple(
                 self,
                 [
@@ -502,6 +610,17 @@ impl MerchantConnectorAccountInterface for Store {
 
 #[async_trait::async_trait]
 impl MerchantConnectorAccountInterface for MockDb {
+    async fn update_multiple_merchant_connector_accounts(
+        &self,
+        _merchant_connector_accounts: Vec<(
+            domain::MerchantConnectorAccount,
+            storage::MerchantConnectorAccountUpdateInternal,
+        )>,
+    ) -> CustomResult<(), errors::StorageError> {
+        // No need to implement this function for `MockDb` as this function will be removed after the
+        // apple pay certificate migration
+        Err(errors::StorageError::MockDbError)?
+    }
     async fn find_merchant_connector_account_by_merchant_id_connector_label(
         &self,
         merchant_id: &str,
@@ -658,6 +777,7 @@ impl MerchantConnectorAccountInterface for MockDb {
             applepay_verified_domains: t.applepay_verified_domains,
             pm_auth_config: t.pm_auth_config,
             status: t.status,
+            connector_wallets_details: t.connector_wallets_details.map(Encryption::from),
         };
         accounts.push(account.clone());
         account
@@ -857,6 +977,14 @@ mod merchant_connector_account_cache_tests {
             applepay_verified_domains: None,
             pm_auth_config: None,
             status: common_enums::ConnectorStatus::Inactive,
+            connector_wallets_details: Some(
+                domain::types::encrypt(
+                    serde_json::Value::default().into(),
+                    merchant_key.key.get_inner().peek(),
+                )
+                .await
+                .unwrap(),
+            ),
         };
 
         db.insert_merchant_connector_account(mca.clone(), &merchant_key)
diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs
index 7344b85d150..73ec0f1635d 100644
--- a/crates/router/src/lib.rs
+++ b/crates/router/src/lib.rs
@@ -144,6 +144,7 @@ pub fn mk_app(
             .service(routes::Routing::server(state.clone()))
             .service(routes::Blocklist::server(state.clone()))
             .service(routes::Gsm::server(state.clone()))
+            .service(routes::ApplePayCertificatesMigration::server(state.clone()))
             .service(routes::PaymentLink::server(state.clone()))
             .service(routes::User::server(state.clone()))
             .service(routes::ConnectorOnboarding::server(state.clone()))
diff --git a/crates/router/src/routes.rs b/crates/router/src/routes.rs
index 9f13635ca90..cb229b5c802 100644
--- a/crates/router/src/routes.rs
+++ b/crates/router/src/routes.rs
@@ -1,6 +1,7 @@
 pub mod admin;
 pub mod api_keys;
 pub mod app;
+pub mod apple_pay_certificates_migration;
 #[cfg(feature = "olap")]
 pub mod blocklist;
 pub mod cache;
@@ -58,10 +59,10 @@ pub use self::app::Payouts;
 #[cfg(all(feature = "olap", feature = "recon"))]
 pub use self::app::Recon;
 pub use self::app::{
-    ApiKeys, AppState, BusinessProfile, Cache, Cards, Configs, ConnectorOnboarding, Customers,
-    Disputes, EphemeralKey, Files, Gsm, Health, Mandates, MerchantAccount,
-    MerchantConnectorAccount, PaymentLink, PaymentMethods, Payments, Poll, Refunds, SessionState,
-    User, Webhooks,
+    ApiKeys, AppState, ApplePayCertificatesMigration, BusinessProfile, Cache, Cards, Configs,
+    ConnectorOnboarding, Customers, Disputes, EphemeralKey, Files, Gsm, Health, Mandates,
+    MerchantAccount, MerchantConnectorAccount, PaymentLink, PaymentMethods, Payments, Poll,
+    Refunds, SessionState, User, Webhooks,
 };
 #[cfg(feature = "olap")]
 pub use self::app::{Blocklist, Routing, Verify, WebhookEvents};
diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs
index 2d1b77460a5..cd392756505 100644
--- a/crates/router/src/routes/admin.rs
+++ b/crates/router/src/routes/admin.rs
@@ -413,7 +413,7 @@ pub async fn payment_connector_delete(
         merchant_connector_id,
     })
     .into_inner();
-    api::server_wrap(
+    Box::pin(api::server_wrap(
         flow,
         state,
         &req,
@@ -430,7 +430,7 @@ pub async fn payment_connector_delete(
             req.headers(),
         ),
         api_locking::LockAction::NotApplicable,
-    )
+    ))
     .await
 }
 /// Merchant Account - Toggle KV
diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs
index 5af3a2bef25..5438535a15c 100644
--- a/crates/router/src/routes/app.rs
+++ b/crates/router/src/routes/app.rs
@@ -29,8 +29,8 @@ use super::routing as cloud_routing;
 use super::verification::{apple_pay_merchant_registration, retrieve_apple_pay_verified_domains};
 #[cfg(feature = "olap")]
 use super::{
-    admin::*, api_keys::*, connector_onboarding::*, disputes::*, files::*, gsm::*, payment_link::*,
-    user::*, user_role::*, webhook_events::*,
+    admin::*, api_keys::*, apple_pay_certificates_migration, connector_onboarding::*, disputes::*,
+    files::*, gsm::*, payment_link::*, user::*, user_role::*, webhook_events::*,
 };
 use super::{cache::*, health::*};
 #[cfg(any(feature = "olap", feature = "oltp"))]
@@ -1117,6 +1117,19 @@ impl Configs {
     }
 }
 
+pub struct ApplePayCertificatesMigration;
+
+#[cfg(feature = "olap")]
+impl ApplePayCertificatesMigration {
+    pub fn server(state: AppState) -> Scope {
+        web::scope("/apple_pay_certificates_migration")
+            .app_data(web::Data::new(state))
+            .service(web::resource("").route(
+                web::post().to(apple_pay_certificates_migration::apple_pay_certificates_migration),
+            ))
+    }
+}
+
 pub struct Poll;
 
 #[cfg(feature = "oltp")]
diff --git a/crates/router/src/routes/apple_pay_certificates_migration.rs b/crates/router/src/routes/apple_pay_certificates_migration.rs
new file mode 100644
index 00000000000..8c9d507ba41
--- /dev/null
+++ b/crates/router/src/routes/apple_pay_certificates_migration.rs
@@ -0,0 +1,30 @@
+use actix_web::{web, HttpRequest, HttpResponse};
+use router_env::Flow;
+
+use super::AppState;
+use crate::{
+    core::{api_locking, apple_pay_certificates_migration},
+    services::{api, authentication as auth},
+};
+
+pub async fn apple_pay_certificates_migration(
+    state: web::Data<AppState>,
+    req: HttpRequest,
+    json_payload: web::Json<
+        api_models::apple_pay_certificates_migration::ApplePayCertificatesMigrationRequest,
+    >,
+) -> HttpResponse {
+    let flow = Flow::ApplePayCertificatesMigration;
+    Box::pin(api::server_wrap(
+        flow,
+        state,
+        &req,
+        &json_payload.into_inner(),
+        |state, _, req, _| {
+            apple_pay_certificates_migration::apple_pay_certificates_migration(state, req)
+        },
+        &auth::AdminApiAuth,
+        api_locking::LockAction::NotApplicable,
+    ))
+    .await
+}
diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs
index b4b9658a7e0..a64343757b5 100644
--- a/crates/router/src/routes/lock_utils.rs
+++ b/crates/router/src/routes/lock_utils.rs
@@ -35,6 +35,7 @@ pub enum ApiIdentifier {
     ConnectorOnboarding,
     Recon,
     Poll,
+    ApplePayCertificatesMigration,
 }
 
 impl From<Flow> for ApiIdentifier {
@@ -186,6 +187,8 @@ impl From<Flow> for ApiIdentifier {
             | Flow::GsmRuleUpdate
             | Flow::GsmRuleDelete => Self::Gsm,
 
+            Flow::ApplePayCertificatesMigration => Self::ApplePayCertificatesMigration,
+
             Flow::UserConnectAccount
             | Flow::UserSignUp
             | Flow::UserSignIn
diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs
index e17ec791637..21aea14caf5 100644
--- a/crates/router/src/types.rs
+++ b/crates/router/src/types.rs
@@ -805,6 +805,7 @@ impl<F1, F2, T1, T2> ForeignFrom<(&RouterData<F1, T1, PaymentsResponseData>, T2)
             address: data.address.clone(),
             auth_type: data.auth_type,
             connector_meta_data: data.connector_meta_data.clone(),
+            connector_wallets_details: data.connector_wallets_details.clone(),
             amount_captured: data.amount_captured,
             access_token: data.access_token.clone(),
             response: data.response.clone(),
@@ -865,6 +866,7 @@ impl<F1, F2>
             address: data.address.clone(),
             auth_type: data.auth_type,
             connector_meta_data: data.connector_meta_data.clone(),
+            connector_wallets_details: data.connector_wallets_details.clone(),
             amount_captured: data.amount_captured,
             access_token: data.access_token.clone(),
             response: data.response.clone(),
diff --git a/crates/router/src/types/api/verify_connector.rs b/crates/router/src/types/api/verify_connector.rs
index 0b932f9aeb1..7e296d0b4a8 100644
--- a/crates/router/src/types/api/verify_connector.rs
+++ b/crates/router/src/types/api/verify_connector.rs
@@ -85,6 +85,7 @@ impl VerifyConnectorData {
             connector_customer: None,
             connector_auth_type: self.connector_auth.clone(),
             connector_meta_data: None,
+            connector_wallets_details: None,
             payment_method_token: None,
             connector_api_version: None,
             recurring_mandate_payment_data: None,
diff --git a/crates/router/src/types/domain/merchant_connector_account.rs b/crates/router/src/types/domain/merchant_connector_account.rs
index 0e7f5b081c3..6c2f6a06e1e 100644
--- a/crates/router/src/types/domain/merchant_connector_account.rs
+++ b/crates/router/src/types/domain/merchant_connector_account.rs
@@ -11,7 +11,10 @@ use diesel_models::{
 use error_stack::ResultExt;
 use masking::{PeekInterface, Secret};
 
-use super::{behaviour, types::TypeEncryption};
+use super::{
+    behaviour,
+    types::{self, AsyncLift, TypeEncryption},
+};
 #[derive(Clone, Debug)]
 pub struct MerchantConnectorAccount {
     pub id: Option<i32>,
@@ -36,6 +39,7 @@ pub struct MerchantConnectorAccount {
     pub applepay_verified_domains: Option<Vec<String>>,
     pub pm_auth_config: Option<serde_json::Value>,
     pub status: enums::ConnectorStatus,
+    pub connector_wallets_details: Option<Encryptable<Secret<serde_json::Value>>>,
 }
 
 #[derive(Debug)]
@@ -56,6 +60,10 @@ pub enum MerchantConnectorAccountUpdate {
         pm_auth_config: Option<serde_json::Value>,
         connector_label: Option<String>,
         status: Option<enums::ConnectorStatus>,
+        connector_wallets_details: Option<Encryptable<Secret<serde_json::Value>>>,
+    },
+    ConnectorWalletDetailsUpdate {
+        connector_wallets_details: Encryptable<Secret<serde_json::Value>>,
     },
 }
 
@@ -92,6 +100,7 @@ impl behaviour::Conversion for MerchantConnectorAccount {
                 applepay_verified_domains: self.applepay_verified_domains,
                 pm_auth_config: self.pm_auth_config,
                 status: self.status,
+                connector_wallets_details: self.connector_wallets_details.map(Encryption::from),
             },
         )
     }
@@ -132,6 +141,13 @@ impl behaviour::Conversion for MerchantConnectorAccount {
             applepay_verified_domains: other.applepay_verified_domains,
             pm_auth_config: other.pm_auth_config,
             status: other.status,
+            connector_wallets_details: other
+                .connector_wallets_details
+                .async_lift(|inner| types::decrypt(inner, key.peek()))
+                .await
+                .change_context(ValidationError::InvalidValue {
+                    message: "Failed while decrypting connector wallets details".to_string(),
+                })?,
         })
     }
 
@@ -160,6 +176,7 @@ impl behaviour::Conversion for MerchantConnectorAccount {
             applepay_verified_domains: self.applepay_verified_domains,
             pm_auth_config: self.pm_auth_config,
             status: self.status,
+            connector_wallets_details: self.connector_wallets_details.map(Encryption::from),
         })
     }
 }
@@ -183,6 +200,7 @@ impl From<MerchantConnectorAccountUpdate> for MerchantConnectorAccountUpdateInte
                 pm_auth_config,
                 connector_label,
                 status,
+                connector_wallets_details,
             } => Self {
                 merchant_id,
                 connector_type,
@@ -201,6 +219,29 @@ impl From<MerchantConnectorAccountUpdate> for MerchantConnectorAccountUpdateInte
                 pm_auth_config,
                 connector_label,
                 status,
+                connector_wallets_details: connector_wallets_details.map(Encryption::from),
+            },
+            MerchantConnectorAccountUpdate::ConnectorWalletDetailsUpdate {
+                connector_wallets_details,
+            } => Self {
+                connector_wallets_details: Some(Encryption::from(connector_wallets_details)),
+                merchant_id: None,
+                connector_type: None,
+                connector_name: None,
+                connector_account_details: None,
+                connector_label: None,
+                test_mode: None,
+                disabled: None,
+                merchant_connector_id: None,
+                payment_methods_enabled: None,
+                frm_configs: None,
+                metadata: None,
+                modified_at: None,
+                connector_webhook_details: None,
+                frm_config: None,
+                applepay_verified_domains: None,
+                pm_auth_config: None,
+                status: None,
             },
         }
     }
diff --git a/crates/router/src/types/domain/payments.rs b/crates/router/src/types/domain/payments.rs
index 51d3210a70f..021505c644f 100644
--- a/crates/router/src/types/domain/payments.rs
+++ b/crates/router/src/types/domain/payments.rs
@@ -1,10 +1,10 @@
 pub use hyperswitch_domain_models::payment_method_data::{
-    AliPayQr, ApplePayThirdPartySdkData, ApplePayWalletData, ApplepayPaymentMethod, BankDebitData,
-    BankRedirectData, BankTransferData, BoletoVoucherData, Card, CardRedirectData, CardToken,
-    CashappQr, CryptoData, GcashRedirection, GiftCardData, GiftCardDetails, GoPayRedirection,
-    GooglePayPaymentMethodInfo, GooglePayRedirectData, GooglePayThirdPartySdkData,
-    GooglePayWalletData, GpayTokenizationData, IndomaretVoucherData, KakaoPayRedirection,
-    MbWayRedirection, PayLaterData, PaymentMethodData, SamsungPayWalletData,
+    AliPayQr, ApplePayFlow, ApplePayThirdPartySdkData, ApplePayWalletData, ApplepayPaymentMethod,
+    BankDebitData, BankRedirectData, BankTransferData, BoletoVoucherData, Card, CardRedirectData,
+    CardToken, CashappQr, CryptoData, GcashRedirection, GiftCardData, GiftCardDetails,
+    GoPayRedirection, GooglePayPaymentMethodInfo, GooglePayRedirectData,
+    GooglePayThirdPartySdkData, GooglePayWalletData, GpayTokenizationData, IndomaretVoucherData,
+    KakaoPayRedirection, MbWayRedirection, PayLaterData, PaymentMethodData, SamsungPayWalletData,
     SepaAndBacsBillingDetails, SwishQrData, TouchNGoRedirection, UpiCollectData, UpiData,
     UpiIntentData, VoucherData, WalletData, WeChatPayQr,
 };
diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs
index b83d3138b7c..e71219b6a9e 100644
--- a/crates/router/src/utils.rs
+++ b/crates/router/src/utils.rs
@@ -707,13 +707,13 @@ impl CustomerAddress for api_models::customers::CustomerRequest {
 }
 
 pub fn add_apple_pay_flow_metrics(
-    apple_pay_flow: &Option<enums::ApplePayFlow>,
+    apple_pay_flow: &Option<domain::ApplePayFlow>,
     connector: Option<String>,
     merchant_id: String,
 ) {
     if let Some(flow) = apple_pay_flow {
         match flow {
-            enums::ApplePayFlow::Simplified => metrics::APPLE_PAY_SIMPLIFIED_FLOW.add(
+            domain::ApplePayFlow::Simplified(_) => metrics::APPLE_PAY_SIMPLIFIED_FLOW.add(
                 &metrics::CONTEXT,
                 1,
                 &[
@@ -724,7 +724,7 @@ pub fn add_apple_pay_flow_metrics(
                     metrics::request::add_attributes("merchant_id", merchant_id.to_owned()),
                 ],
             ),
-            enums::ApplePayFlow::Manual => metrics::APPLE_PAY_MANUAL_FLOW.add(
+            domain::ApplePayFlow::Manual => metrics::APPLE_PAY_MANUAL_FLOW.add(
                 &metrics::CONTEXT,
                 1,
                 &[
@@ -741,14 +741,14 @@ pub fn add_apple_pay_flow_metrics(
 
 pub fn add_apple_pay_payment_status_metrics(
     payment_attempt_status: enums::AttemptStatus,
-    apple_pay_flow: Option<enums::ApplePayFlow>,
+    apple_pay_flow: Option<domain::ApplePayFlow>,
     connector: Option<String>,
     merchant_id: String,
 ) {
     if payment_attempt_status == enums::AttemptStatus::Charged {
         if let Some(flow) = apple_pay_flow {
             match flow {
-                enums::ApplePayFlow::Simplified => {
+                domain::ApplePayFlow::Simplified(_) => {
                     metrics::APPLE_PAY_SIMPLIFIED_FLOW_SUCCESSFUL_PAYMENT.add(
                         &metrics::CONTEXT,
                         1,
@@ -761,7 +761,7 @@ pub fn add_apple_pay_payment_status_metrics(
                         ],
                     )
                 }
-                enums::ApplePayFlow::Manual => metrics::APPLE_PAY_MANUAL_FLOW_SUCCESSFUL_PAYMENT
+                domain::ApplePayFlow::Manual => metrics::APPLE_PAY_MANUAL_FLOW_SUCCESSFUL_PAYMENT
                     .add(
                         &metrics::CONTEXT,
                         1,
@@ -778,7 +778,7 @@ pub fn add_apple_pay_payment_status_metrics(
     } else if payment_attempt_status == enums::AttemptStatus::Failure {
         if let Some(flow) = apple_pay_flow {
             match flow {
-                enums::ApplePayFlow::Simplified => {
+                domain::ApplePayFlow::Simplified(_) => {
                     metrics::APPLE_PAY_SIMPLIFIED_FLOW_FAILED_PAYMENT.add(
                         &metrics::CONTEXT,
                         1,
@@ -791,7 +791,7 @@ pub fn add_apple_pay_payment_status_metrics(
                         ],
                     )
                 }
-                enums::ApplePayFlow::Manual => metrics::APPLE_PAY_MANUAL_FLOW_FAILED_PAYMENT.add(
+                domain::ApplePayFlow::Manual => metrics::APPLE_PAY_MANUAL_FLOW_FAILED_PAYMENT.add(
                     &metrics::CONTEXT,
                     1,
                     &[
diff --git a/crates/router/tests/connectors/aci.rs b/crates/router/tests/connectors/aci.rs
index 10e8b366530..b01c5abb968 100644
--- a/crates/router/tests/connectors/aci.rs
+++ b/crates/router/tests/connectors/aci.rs
@@ -97,6 +97,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData {
             None,
         ),
         connector_meta_data: None,
+        connector_wallets_details: None,
         amount_captured: None,
         access_token: None,
         session_token: None,
@@ -159,6 +160,7 @@ fn construct_refund_router_data<F>() -> types::RefundsRouterData<F> {
         response: Err(types::ErrorResponse::default()),
         address: PaymentAddress::default(),
         connector_meta_data: None,
+        connector_wallets_details: None,
         amount_captured: None,
         access_token: None,
         session_token: None,
diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs
index 0de19f17b54..be86d377299 100644
--- a/crates/router/tests/connectors/utils.rs
+++ b/crates/router/tests/connectors/utils.rs
@@ -540,6 +540,7 @@ pub trait ConnectorActions: Connector {
             connector_meta_data: info
                 .clone()
                 .and_then(|a| a.connector_meta_data.map(Secret::new)),
+            connector_wallets_details: None,
             amount_captured: None,
             access_token: info.clone().and_then(|a| a.access_token),
             session_token: None,
diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs
index 110532f524d..51f762e3713 100644
--- a/crates/router_env/src/logger/types.rs
+++ b/crates/router_env/src/logger/types.rs
@@ -298,6 +298,8 @@ pub enum Flow {
     GsmRuleRetrieve,
     /// Gsm Rule Update flow
     GsmRuleUpdate,
+    /// Apple pay certificates migration
+    ApplePayCertificatesMigration,
     /// Gsm Rule Delete flow
     GsmRuleDelete,
     /// User Sign Up
diff --git a/crates/storage_impl/src/errors.rs b/crates/storage_impl/src/errors.rs
index 4356b6b7997..b153c47b882 100644
--- a/crates/storage_impl/src/errors.rs
+++ b/crates/storage_impl/src/errors.rs
@@ -101,6 +101,12 @@ impl From<error_stack::Report<RedisError>> for StorageError {
     }
 }
 
+impl From<diesel::result::Error> for StorageError {
+    fn from(err: diesel::result::Error) -> Self {
+        Self::from(error_stack::report!(DatabaseError::from(err)))
+    }
+}
+
 impl From<error_stack::Report<DatabaseError>> for StorageError {
     fn from(err: error_stack::Report<DatabaseError>) -> Self {
         Self::DatabaseError(err)
diff --git a/migrations/2024-05-28-054439_connector_wallets_details/down.sql b/migrations/2024-05-28-054439_connector_wallets_details/down.sql
new file mode 100644
index 00000000000..dea26bbd1eb
--- /dev/null
+++ b/migrations/2024-05-28-054439_connector_wallets_details/down.sql
@@ -0,0 +1,2 @@
+-- This file should undo anything in `up.sql`
+ALTER TABLE merchant_connector_account DROP COLUMN IF EXISTS connector_wallets_details;
\ No newline at end of file
diff --git a/migrations/2024-05-28-054439_connector_wallets_details/up.sql b/migrations/2024-05-28-054439_connector_wallets_details/up.sql
new file mode 100644
index 00000000000..c75de9204df
--- /dev/null
+++ b/migrations/2024-05-28-054439_connector_wallets_details/up.sql
@@ -0,0 +1,2 @@
+-- Your SQL goes here
+ALTER TABLE merchant_connector_account ADD COLUMN IF NOT EXISTS connector_wallets_details BYTEA DEFAULT NULL;
\ No newline at end of file
diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json
index f42481798a4..ba0d8946192 100644
--- a/openapi/openapi_spec.json
+++ b/openapi/openapi_spec.json
@@ -13697,6 +13697,63 @@
         },
         "additionalProperties": false
       },
+      "PaymentProcessingDetails": {
+        "type": "object",
+        "required": [
+          "payment_processing_certificate",
+          "payment_processing_certificate_key"
+        ],
+        "properties": {
+          "payment_processing_certificate": {
+            "type": "string"
+          },
+          "payment_processing_certificate_key": {
+            "type": "string"
+          }
+        }
+      },
+      "PaymentProcessingDetailsAt": {
+        "oneOf": [
+          {
+            "allOf": [
+              {
+                "$ref": "#/components/schemas/PaymentProcessingDetails"
+              },
+              {
+                "type": "object",
+                "required": [
+                  "payment_processing_details_at"
+                ],
+                "properties": {
+                  "payment_processing_details_at": {
+                    "type": "string",
+                    "enum": [
+                      "Hyperswitch"
+                    ]
+                  }
+                }
+              }
+            ]
+          },
+          {
+            "type": "object",
+            "required": [
+              "payment_processing_details_at"
+            ],
+            "properties": {
+              "payment_processing_details_at": {
+                "type": "string",
+                "enum": [
+                  "Connector"
+                ]
+              }
+            }
+          }
+        ],
+        "discriminator": {
+          "propertyName": "payment_processing_details_at"
+        }
+      },
       "PaymentRetrieveBody": {
         "type": "object",
         "properties": {
@@ -18394,43 +18451,55 @@
         }
       },
       "SessionTokenInfo": {
-        "type": "object",
-        "required": [
-          "certificate",
-          "certificate_keys",
-          "merchant_identifier",
-          "display_name",
-          "initiative",
-          "initiative_context"
-        ],
-        "properties": {
-          "certificate": {
-            "type": "string"
-          },
-          "certificate_keys": {
-            "type": "string"
-          },
-          "merchant_identifier": {
-            "type": "string"
-          },
-          "display_name": {
-            "type": "string"
-          },
-          "initiative": {
-            "type": "string"
-          },
-          "initiative_context": {
-            "type": "string"
-          },
-          "merchant_business_country": {
+        "allOf": [
+          {
             "allOf": [
               {
-                "$ref": "#/components/schemas/CountryAlpha2"
+                "$ref": "#/components/schemas/PaymentProcessingDetailsAt"
               }
             ],
             "nullable": true
+          },
+          {
+            "type": "object",
+            "required": [
+              "certificate",
+              "certificate_keys",
+              "merchant_identifier",
+              "display_name",
+              "initiative",
+              "initiative_context"
+            ],
+            "properties": {
+              "certificate": {
+                "type": "string"
+              },
+              "certificate_keys": {
+                "type": "string"
+              },
+              "merchant_identifier": {
+                "type": "string"
+              },
+              "display_name": {
+                "type": "string"
+              },
+              "initiative": {
+                "type": "string"
+              },
+              "initiative_context": {
+                "type": "string"
+              },
+              "merchant_business_country": {
+                "allOf": [
+                  {
+                    "$ref": "#/components/schemas/CountryAlpha2"
+                  }
+                ],
+                "nullable": true
+              }
+            }
           }
-        }
+        ]
       },
       "StraightThroughAlgorithm": {
         "oneOf": [

## 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 -->
Currently apple pay related details are being stored in the connector metadata, as in future we are going to add apple pay decrypted flow support for ios app we will be collecting the apple pay payment processing certificates as well. As this ppc and ppc key needs to be securely stored, we need to move the existing certificates to a new column which stores the encrypted certificates.

This pr adds a api end point (`/apple_pay_certificates_migration`) to migrate the apple pay details form the merchant connector account `metadata`  to the newly added column connector (`connector_wallets_details`). This api takes list of `merchant_ids` as input and then lists all the configured merchant connector accounts for that merchant id. After which it checks for the apple pay details in the metadata in the merchant connector account, if present it is encrypted by merchant's DEK (Data Encryption Key) and will be stored in the newly added column (`connector_wallets_details`) in the merchant connector account. Response contains the list of merchant_ids for migration succeeded (`migration_successful`) and failed (`migraiton_failed`).

### 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)?
-->
-> Apple pay certificate migration api
```
curl --location 'http://localhost:8080/apple_pay_certificates_migration' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: test_admin' \
--data '{
    "merchant_ids": ["merchant_1713176513", "merchant_1714122879"]
}'
```
<img width="719" alt="image" src="https://github.com/juspay/hyperswitch/assets/83439957/bb66cc2c-69f7-4c6e-bb3c-1e184944bbd2">
-> Check the db for connector_wallets_details of mca belonging to above merchants
<img width="862" alt="image" src="https://github.com/juspay/hyperswitch/assets/83439957/24d5c9ed-f091-49cd-983b-839724024118">

-> Create MCA with apple pay `simplified` and manual `flow`
metadata for apple pay `simplified`
```
"apple_pay_combined": {
            "simplified": {
                "session_token_data": {
                    "initiative_context": "sdk-test-app.netlify.app",
                    "merchant_business_country": "US"
                },
                "payment_request_data": {
                    "label": "applepay",
                    "supported_networks": [
                        "visa",
                        "masterCard",
                        "amex",
                        "discover"
                    ],
                    "merchant_capabilities": [
                        "supports3DS"
                    ]
                }
            }
        }
```
metadata for apple pay `manual` flow
```
"apple_pay_combined": {
            "manual": {
                "session_token_data": {
                    "initiative": "web",
                    "certificate": "",
                    "display_name": "applepay",
                    "certificate_keys": "",
                    "merchant_business_coountry": "US"
                },
                "payment_request_data": {
                    "label": "applepay",
                    "supported_networks": [
                        "visa",
                        "masterCard",
                        "amex",
                        "discover"
                    ],
                    "merchant_capabilities": [
                        "supports3DS"
                    ]
                }
            }
        }
```
-> Confirm an apple pay payment with above flows
<img width="1159" alt="image" src="https://github.com/juspay/hyperswitch/assets/83439957/a70ebc44-948a-421d-bcdf-3f9a2adb4361">
-> Create a apple pay payment with the below metadata
```
"apple_pay_combined": {
            "manual": {
                "session_token_data": {
                    "initiative": "web",
                    "certificate": "",
                    "display_name": "applepay",
                    "certificate_keys": "",
                    "payment_processing_details_at": "Hyperswitch",
                    "payment_processing_certificate": "",
                    "payment_processing_certificate_key": "",
                    "initiative_context": "sdk-test-app.netlify.app",
                    "merchant_identifier": "",
                    "merchant_business_coountry": "US"
                },
                "payment_request_data": {
                    "label": "applepay",
                    "supported_networks": [
                        "visa",
                        "masterCard",
                        "amex",
                        "discover"
                    ],
                    "merchant_capabilities": [
                        "supports3DS"
                    ]
                }
            }
        }
```
-> Confirm apple pay payment
<img width="1055" alt="image" src="https://github.com/juspay/hyperswitch/assets/83439957/4460d4e3-24e9-4348-ac1c-c05d6bef5e3e">


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

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