Bug: [FEATURE] [Network Tokenization]: Tokenize before payment



### Feature Description

Tokenize the card before payment based on profile level config.

### Possible Implementation

Tokenize the card before payment based on profile level config.

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

- [X] I checked and didn't find a similar issue

### Have you read the Contributing Guidelines?

- [X] I have read the [Contributing Guidelines](https://github.com/juspay/hyperswitch/blob/main/docs/CONTRIBUTING.md)

### Are you willing to submit a PR?

Yes, I am willing to submit a PR!
diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs
index a7b57b7433c..60eca62b618 100644
--- a/crates/api_models/src/admin.rs
+++ b/crates/api_models/src/admin.rs
@@ -2002,6 +2002,9 @@ pub struct ProfileCreate {
 
     /// Indicates if the redirection has to open in the iframe
     pub is_iframe_redirection_enabled: Option<bool>,
+
+    /// Indicates if pre network tokenization is enabled or not
+    pub is_pre_network_tokenization_enabled: Option<bool>,
 }
 
 #[nutype::nutype(
@@ -2302,6 +2305,10 @@ pub struct ProfileResponse {
     //Merchant country for the profile
     #[schema(value_type = Option<CountryAlpha2>, example = "US")]
     pub merchant_business_country: Option<api_enums::CountryAlpha2>,
+
+    /// Indicates if pre network tokenization is enabled or not
+    #[schema(default = false, example = false)]
+    pub is_pre_network_tokenization_enabled: bool,
 }
 
 #[cfg(feature = "v2")]
@@ -2602,6 +2609,10 @@ pub struct ProfileUpdate {
 
     /// Indicates if the redirection has to open in the iframe
     pub is_iframe_redirection_enabled: Option<bool>,
+
+    /// Indicates if pre network tokenization is enabled or not
+    #[schema(default = false, example = false)]
+    pub is_pre_network_tokenization_enabled: Option<bool>,
 }
 
 #[cfg(feature = "v2")]
diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs
index 9309bb42871..a7c606fb94b 100644
--- a/crates/diesel_models/src/business_profile.rs
+++ b/crates/diesel_models/src/business_profile.rs
@@ -71,6 +71,7 @@ pub struct Profile {
     pub merchant_business_country: Option<common_enums::CountryAlpha2>,
     pub id: Option<common_utils::id_type::ProfileId>,
     pub is_iframe_redirection_enabled: Option<bool>,
+    pub is_pre_network_tokenization_enabled: Option<bool>,
 }
 
 #[cfg(feature = "v1")]
@@ -125,6 +126,7 @@ pub struct ProfileNew {
     pub merchant_business_country: Option<common_enums::CountryAlpha2>,
     pub id: Option<common_utils::id_type::ProfileId>,
     pub is_iframe_redirection_enabled: Option<bool>,
+    pub is_pre_network_tokenization_enabled: Option<bool>,
 }
 
 #[cfg(feature = "v1")]
@@ -177,6 +179,7 @@ pub struct ProfileUpdateInternal {
     pub is_debit_routing_enabled: bool,
     pub merchant_business_country: Option<common_enums::CountryAlpha2>,
     pub is_iframe_redirection_enabled: Option<bool>,
+    pub is_pre_network_tokenization_enabled: Option<bool>,
 }
 
 #[cfg(feature = "v1")]
@@ -226,6 +229,7 @@ impl ProfileUpdateInternal {
             is_debit_routing_enabled,
             merchant_business_country,
             is_iframe_redirection_enabled,
+            is_pre_network_tokenization_enabled,
         } = self;
         Profile {
             profile_id: source.profile_id,
@@ -303,6 +307,8 @@ impl ProfileUpdateInternal {
                 .or(source.merchant_business_country),
             is_iframe_redirection_enabled: is_iframe_redirection_enabled
                 .or(source.is_iframe_redirection_enabled),
+            is_pre_network_tokenization_enabled: is_pre_network_tokenization_enabled
+                .or(source.is_pre_network_tokenization_enabled),
         }
     }
 }
diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs
index f890963c18a..5994670e1d5 100644
--- a/crates/diesel_models/src/schema.rs
+++ b/crates/diesel_models/src/schema.rs
@@ -226,6 +226,7 @@ diesel::table! {
         #[max_length = 64]
         id -> Nullable<Varchar>,
         is_iframe_redirection_enabled -> Nullable<Bool>,
+        is_pre_network_tokenization_enabled -> Nullable<Bool>,
     }
 }
 
diff --git a/crates/hyperswitch_domain_models/src/business_profile.rs b/crates/hyperswitch_domain_models/src/business_profile.rs
index 9f70f462386..2c8b2e99792 100644
--- a/crates/hyperswitch_domain_models/src/business_profile.rs
+++ b/crates/hyperswitch_domain_models/src/business_profile.rs
@@ -74,6 +74,7 @@ pub struct Profile {
     pub is_debit_routing_enabled: bool,
     pub merchant_business_country: Option<common_enums::CountryAlpha2>,
     pub is_iframe_redirection_enabled: Option<bool>,
+    pub is_pre_network_tokenization_enabled: bool,
 }
 
 #[cfg(feature = "v1")]
@@ -126,6 +127,7 @@ pub struct ProfileSetter {
     pub is_debit_routing_enabled: bool,
     pub merchant_business_country: Option<api_enums::CountryAlpha2>,
     pub is_iframe_redirection_enabled: Option<bool>,
+    pub is_pre_network_tokenization_enabled: bool,
 }
 
 #[cfg(feature = "v1")]
@@ -183,6 +185,7 @@ impl From<ProfileSetter> for Profile {
             is_debit_routing_enabled: value.is_debit_routing_enabled,
             merchant_business_country: value.merchant_business_country,
             is_iframe_redirection_enabled: value.is_iframe_redirection_enabled,
+            is_pre_network_tokenization_enabled: value.is_pre_network_tokenization_enabled,
         }
     }
 }
@@ -242,6 +245,7 @@ pub struct ProfileGeneralUpdate {
     pub is_debit_routing_enabled: bool,
     pub merchant_business_country: Option<api_enums::CountryAlpha2>,
     pub is_iframe_redirection_enabled: Option<bool>,
+    pub is_pre_network_tokenization_enabled: Option<bool>,
 }
 
 #[cfg(feature = "v1")]
@@ -316,6 +320,7 @@ impl From<ProfileUpdate> for ProfileUpdateInternal {
                     is_debit_routing_enabled,
                     merchant_business_country,
                     is_iframe_redirection_enabled,
+                    is_pre_network_tokenization_enabled,
                 } = *update;
 
                 Self {
@@ -363,6 +368,7 @@ impl From<ProfileUpdate> for ProfileUpdateInternal {
                     is_debit_routing_enabled,
                     merchant_business_country,
                     is_iframe_redirection_enabled,
+                    is_pre_network_tokenization_enabled,
                 }
             }
             ProfileUpdate::RoutingAlgorithmUpdate {
@@ -412,6 +418,7 @@ impl From<ProfileUpdate> for ProfileUpdateInternal {
                 is_debit_routing_enabled: false,
                 merchant_business_country: None,
                 is_iframe_redirection_enabled: None,
+                is_pre_network_tokenization_enabled: None,
             },
             ProfileUpdate::DynamicRoutingAlgorithmUpdate {
                 dynamic_routing_algorithm,
@@ -459,6 +466,7 @@ impl From<ProfileUpdate> for ProfileUpdateInternal {
                 is_debit_routing_enabled: false,
                 merchant_business_country: None,
                 is_iframe_redirection_enabled: None,
+                is_pre_network_tokenization_enabled: None,
             },
             ProfileUpdate::ExtendedCardInfoUpdate {
                 is_extended_card_info_enabled,
@@ -506,6 +514,7 @@ impl From<ProfileUpdate> for ProfileUpdateInternal {
                 is_debit_routing_enabled: false,
                 merchant_business_country: None,
                 is_iframe_redirection_enabled: None,
+                is_pre_network_tokenization_enabled: None,
             },
             ProfileUpdate::ConnectorAgnosticMitUpdate {
                 is_connector_agnostic_mit_enabled,
@@ -553,6 +562,7 @@ impl From<ProfileUpdate> for ProfileUpdateInternal {
                 is_debit_routing_enabled: false,
                 merchant_business_country: None,
                 is_iframe_redirection_enabled: None,
+                is_pre_network_tokenization_enabled: None,
             },
             ProfileUpdate::NetworkTokenizationUpdate {
                 is_network_tokenization_enabled,
@@ -600,6 +610,7 @@ impl From<ProfileUpdate> for ProfileUpdateInternal {
                 is_debit_routing_enabled: false,
                 merchant_business_country: None,
                 is_iframe_redirection_enabled: None,
+                is_pre_network_tokenization_enabled: None,
             },
             ProfileUpdate::CardTestingSecretKeyUpdate {
                 card_testing_secret_key,
@@ -647,6 +658,7 @@ impl From<ProfileUpdate> for ProfileUpdateInternal {
                 is_debit_routing_enabled: false,
                 merchant_business_country: None,
                 is_iframe_redirection_enabled: None,
+                is_pre_network_tokenization_enabled: None,
             },
         }
     }
@@ -714,6 +726,7 @@ impl super::behaviour::Conversion for Profile {
             is_debit_routing_enabled: self.is_debit_routing_enabled,
             merchant_business_country: self.merchant_business_country,
             is_iframe_redirection_enabled: self.is_iframe_redirection_enabled,
+            is_pre_network_tokenization_enabled: Some(self.is_pre_network_tokenization_enabled),
         })
     }
 
@@ -805,6 +818,9 @@ impl super::behaviour::Conversion for Profile {
                 is_debit_routing_enabled: item.is_debit_routing_enabled,
                 merchant_business_country: item.merchant_business_country,
                 is_iframe_redirection_enabled: item.is_iframe_redirection_enabled,
+                is_pre_network_tokenization_enabled: item
+                    .is_pre_network_tokenization_enabled
+                    .unwrap_or(false),
             })
         }
         .await
@@ -867,6 +883,7 @@ impl super::behaviour::Conversion for Profile {
             is_debit_routing_enabled: self.is_debit_routing_enabled,
             merchant_business_country: self.merchant_business_country,
             is_iframe_redirection_enabled: self.is_iframe_redirection_enabled,
+            is_pre_network_tokenization_enabled: Some(self.is_pre_network_tokenization_enabled),
         })
     }
 }
diff --git a/crates/hyperswitch_domain_models/src/payment_method_data.rs b/crates/hyperswitch_domain_models/src/payment_method_data.rs
index 9f62dbbe987..b3e0e9540b3 100644
--- a/crates/hyperswitch_domain_models/src/payment_method_data.rs
+++ b/crates/hyperswitch_domain_models/src/payment_method_data.rs
@@ -2002,3 +2002,37 @@ impl SingleUseTokenKey {
         &self.0
     }
 }
+
+#[cfg(feature = "v1")]
+impl From<Card> for payment_methods::CardDetail {
+    fn from(card_data: Card) -> Self {
+        Self {
+            card_number: card_data.card_number.clone(),
+            card_exp_month: card_data.card_exp_month.clone(),
+            card_exp_year: card_data.card_exp_year.clone(),
+            card_holder_name: None,
+            nick_name: None,
+            card_issuing_country: None,
+            card_network: card_data.card_network.clone(),
+            card_issuer: None,
+            card_type: None,
+        }
+    }
+}
+
+#[cfg(feature = "v1")]
+impl From<NetworkTokenData> for payment_methods::CardDetail {
+    fn from(network_token_data: NetworkTokenData) -> Self {
+        Self {
+            card_number: network_token_data.token_number.clone(),
+            card_exp_month: network_token_data.token_exp_month.clone(),
+            card_exp_year: network_token_data.token_exp_year.clone(),
+            card_holder_name: None,
+            nick_name: None,
+            card_issuing_country: None,
+            card_network: network_token_data.card_network.clone(),
+            card_issuer: None,
+            card_type: None,
+        }
+    }
+}
diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs
index 103ccd593f5..047fe7fb2cf 100644
--- a/crates/hyperswitch_domain_models/src/payments.rs
+++ b/crates/hyperswitch_domain_models/src/payments.rs
@@ -999,9 +999,29 @@ where
     }
 }
 
+#[derive(Default, Clone, serde::Serialize, Debug)]
+pub struct CardAndNetworkTokenDataForVault {
+    pub card_data: payment_method_data::Card,
+    pub network_token: NetworkTokenDataForVault,
+}
+
+#[derive(Default, Clone, serde::Serialize, Debug)]
+pub struct NetworkTokenDataForVault {
+    pub network_token_data: payment_method_data::NetworkTokenData,
+    pub network_token_req_ref_id: String,
+}
+
+#[derive(Default, Clone, serde::Serialize, Debug)]
+pub struct CardDataForVault {
+    pub card_data: payment_method_data::Card,
+    pub network_token_req_ref_id: Option<String>,
+}
+
 #[derive(Clone, serde::Serialize, Debug)]
 pub enum VaultOperation {
     ExistingVaultData(VaultData),
+    SaveCardData(CardDataForVault),
+    SaveCardAndNetworkTokenData(Box<CardAndNetworkTokenDataForVault>),
 }
 
 impl VaultOperation {
diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs
index f12dc48924c..db8a44df16e 100644
--- a/crates/router/src/core/admin.rs
+++ b/crates/router/src/core/admin.rs
@@ -3924,6 +3924,9 @@ impl ProfileCreateBridge for api::ProfileCreate {
             is_debit_routing_enabled: self.is_debit_routing_enabled.unwrap_or_default(),
             merchant_business_country: self.merchant_business_country,
             is_iframe_redirection_enabled: self.is_iframe_redirection_enabled,
+            is_pre_network_tokenization_enabled: self
+                .is_pre_network_tokenization_enabled
+                .unwrap_or_default(),
         }))
     }
 
@@ -4373,6 +4376,7 @@ impl ProfileUpdateBridge for api::ProfileUpdate {
                 is_debit_routing_enabled: self.is_debit_routing_enabled.unwrap_or_default(),
                 merchant_business_country: self.merchant_business_country,
                 is_iframe_redirection_enabled: self.is_iframe_redirection_enabled,
+                is_pre_network_tokenization_enabled: self.is_pre_network_tokenization_enabled,
             },
         )))
     }
diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs
index f5350e81d5f..e1567054aab 100644
--- a/crates/router/src/core/payments.rs
+++ b/crates/router/src/core/payments.rs
@@ -55,7 +55,7 @@ pub use hyperswitch_domain_models::{
     router_request_types::CustomerDetails,
 };
 use hyperswitch_domain_models::{
-    payments::{payment_intent::CustomerData, ClickToPayMetaData},
+    payments::{self, payment_intent::CustomerData, ClickToPayMetaData},
     router_data::AccessToken,
 };
 use masking::{ExposeInterface, PeekInterface, Secret};
@@ -478,6 +478,7 @@ where
                 }
                 _ => (),
             };
+
             payment_data = match connector_details {
                 ConnectorCallType::PreDetermined(ref connector) => {
                     #[cfg(all(feature = "dynamic_routing", feature = "v1"))]
@@ -498,6 +499,7 @@ where
                     } else {
                         None
                     };
+
                     let (router_data, mca) = call_connector_service(
                         state,
                         req_state.clone(),
@@ -3042,6 +3044,53 @@ where
     )
     .await?;
 
+    let customer_acceptance = payment_data
+        .get_payment_attempt()
+        .customer_acceptance
+        .clone();
+
+    if is_pre_network_tokenization_enabled(
+        state,
+        business_profile,
+        customer_acceptance,
+        connector.connector_name,
+    ) {
+        let payment_method_data = payment_data.get_payment_method_data();
+        let customer_id = payment_data.get_payment_intent().customer_id.clone();
+        if let (Some(domain::PaymentMethodData::Card(card_data)), Some(customer_id)) =
+            (payment_method_data, customer_id)
+        {
+            let vault_operation =
+                get_vault_operation_for_pre_network_tokenization(state, customer_id, card_data)
+                    .await;
+            match vault_operation {
+                payments::VaultOperation::SaveCardAndNetworkTokenData(
+                    card_and_network_token_data,
+                ) => {
+                    payment_data.set_vault_operation(
+                        payments::VaultOperation::SaveCardAndNetworkTokenData(Box::new(
+                            *card_and_network_token_data.clone(),
+                        )),
+                    );
+
+                    payment_data.set_payment_method_data(Some(
+                        domain::PaymentMethodData::NetworkToken(
+                            card_and_network_token_data
+                                .network_token
+                                .network_token_data
+                                .clone(),
+                        ),
+                    ));
+                }
+                payments::VaultOperation::SaveCardData(card_data_for_vault) => payment_data
+                    .set_vault_operation(payments::VaultOperation::SaveCardData(
+                        card_data_for_vault.clone(),
+                    )),
+                payments::VaultOperation::ExistingVaultData(_) => (),
+            }
+        }
+    }
+
     #[cfg(feature = "v1")]
     if payment_data
         .get_payment_attempt()
@@ -4189,7 +4238,7 @@ pub async fn get_session_token_for_click_to_pay(
     merchant_id: &id_type::MerchantId,
     merchant_context: &domain::MerchantContext,
     authentication_product_ids: common_types::payments::AuthenticationConnectorAccountMap,
-    payment_intent: &hyperswitch_domain_models::payments::PaymentIntent,
+    payment_intent: &payments::PaymentIntent,
     profile_id: &id_type::ProfileId,
 ) -> RouterResult<api_models::payments::SessionToken> {
     let click_to_pay_mca_id = authentication_product_ids
@@ -6126,6 +6175,64 @@ where
     Ok(())
 }
 
+#[cfg(feature = "v1")]
+pub fn is_pre_network_tokenization_enabled(
+    state: &SessionState,
+    business_profile: &domain::Profile,
+    customer_acceptance: Option<Secret<serde_json::Value>>,
+    connector_name: enums::Connector,
+) -> bool {
+    let ntid_supported_connectors = &state
+        .conf
+        .network_transaction_id_supported_connectors
+        .connector_list;
+
+    let is_nt_supported_connector = ntid_supported_connectors.contains(&connector_name);
+
+    business_profile.is_network_tokenization_enabled
+        && business_profile.is_pre_network_tokenization_enabled
+        && customer_acceptance.is_some()
+        && is_nt_supported_connector
+}
+
+#[cfg(feature = "v1")]
+pub async fn get_vault_operation_for_pre_network_tokenization(
+    state: &SessionState,
+    customer_id: id_type::CustomerId,
+    card_data: &hyperswitch_domain_models::payment_method_data::Card,
+) -> payments::VaultOperation {
+    let pre_tokenization_response =
+        tokenization::pre_payment_tokenization(state, customer_id, card_data)
+            .await
+            .ok();
+    match pre_tokenization_response {
+        Some((Some(token_response), Some(token_ref))) => {
+            let token_data = domain::NetworkTokenData::from(token_response);
+            let network_token_data_for_vault = payments::NetworkTokenDataForVault {
+                network_token_data: token_data.clone(),
+                network_token_req_ref_id: token_ref,
+            };
+
+            payments::VaultOperation::SaveCardAndNetworkTokenData(Box::new(
+                payments::CardAndNetworkTokenDataForVault {
+                    card_data: card_data.clone(),
+                    network_token: network_token_data_for_vault.clone(),
+                },
+            ))
+        }
+        Some((None, Some(token_ref))) => {
+            payments::VaultOperation::SaveCardData(payments::CardDataForVault {
+                card_data: card_data.clone(),
+                network_token_req_ref_id: Some(token_ref),
+            })
+        }
+        _ => payments::VaultOperation::SaveCardData(payments::CardDataForVault {
+            card_data: card_data.clone(),
+            network_token_req_ref_id: None,
+        }),
+    }
+}
+
 #[cfg(feature = "v1")]
 #[allow(clippy::too_many_arguments)]
 pub async fn get_connector_choice<F, Req, D>(
diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs
index 59b25d9b86b..32158085cde 100644
--- a/crates/router/src/core/payments/helpers.rs
+++ b/crates/router/src/core/payments/helpers.rs
@@ -2608,8 +2608,10 @@ pub async fn make_pm_data<'a, F: Clone, R, D>(
         (_, Some(hyperswitch_token)) => {
             let existing_vault_data = payment_data.get_vault_operation();
 
-            let vault_data = existing_vault_data.map(|data| match data {
-                domain_payments::VaultOperation::ExistingVaultData(vault_data) => vault_data,
+            let vault_data = existing_vault_data.and_then(|data| match data {
+                domain_payments::VaultOperation::ExistingVaultData(vault_data) => Some(vault_data),
+                domain_payments::VaultOperation::SaveCardData(_)
+                | domain_payments::VaultOperation::SaveCardAndNetworkTokenData(_) => None,
             });
 
             let pm_data = Box::pin(payment_methods::retrieve_payment_method_with_token(
diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs
index 0b182d249bd..4c8edfb04ed 100644
--- a/crates/router/src/core/payments/operations/payment_response.rs
+++ b/crates/router/src/core/payments/operations/payment_response.rs
@@ -170,6 +170,8 @@ impl<F: Send + Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsAuthor
             .and_then(|billing_details| billing_details.address.as_ref())
             .and_then(|address| address.get_optional_full_name());
         let mut should_avoid_saving = false;
+        let vault_operation = payment_data.vault_operation.clone();
+        let payment_method_info = payment_data.payment_method_info.clone();
 
         if let Some(payment_method_info) = &payment_data.payment_method_info {
             if payment_data.payment_intent.off_session.is_none() && resp.response.is_ok() {
@@ -207,6 +209,8 @@ impl<F: Send + Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsAuthor
             business_profile,
             connector_mandate_reference_id.clone(),
             merchant_connector_id.clone(),
+            vault_operation.clone(),
+            payment_method_info.clone(),
         ));
 
         let is_connector_mandate = resp.request.customer_acceptance.is_some()
@@ -321,6 +325,8 @@ impl<F: Send + Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsAuthor
                         &business_profile,
                         connector_mandate_reference_id,
                         merchant_connector_id.clone(),
+                        vault_operation.clone(),
+                        payment_method_info.clone(),
                     ))
                     .await;
 
@@ -1178,7 +1184,8 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::SetupMandateRequestDa
             .connector_mandate_detail
             .as_ref()
             .map(|detail| ConnectorMandateReferenceId::foreign_from(detail.clone()));
-
+        let vault_operation = payment_data.vault_operation.clone();
+        let payment_method_info = payment_data.payment_method_info.clone();
         let merchant_connector_id = payment_data.payment_attempt.merchant_connector_id.clone();
         let tokenization::SavePaymentMethodDataResponse {
             payment_method_id,
@@ -1196,6 +1203,8 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::SetupMandateRequestDa
             business_profile,
             connector_mandate_reference_id,
             merchant_connector_id.clone(),
+            vault_operation,
+            payment_method_info,
         ))
         .await?;
 
diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs
index 25008523633..34d2302ab8b 100644
--- a/crates/router/src/core/payments/tokenization.rs
+++ b/crates/router/src/core/payments/tokenization.rs
@@ -13,12 +13,15 @@ use common_enums::{ConnectorMandateStatus, PaymentMethod};
 use common_utils::{
     crypto::Encryptable,
     ext_traits::{AsyncExt, Encode, ValueExt},
-    id_type, pii,
+    id_type,
+    metrics::utils::record_operation_time,
+    pii,
 };
 use error_stack::{report, ResultExt};
 #[cfg(feature = "v1")]
-use hyperswitch_domain_models::mandates::{
-    CommonMandateReference, PaymentsMandateReference, PaymentsMandateReferenceRecord,
+use hyperswitch_domain_models::{
+    mandates::{CommonMandateReference, PaymentsMandateReference, PaymentsMandateReferenceRecord},
+    payment_method_data,
 };
 use masking::{ExposeInterface, Secret};
 use router_env::{instrument, tracing};
@@ -42,7 +45,7 @@ use crate::{
     types::{
         self,
         api::{self, CardDetailFromLocker, CardDetailsPaymentMethod, PaymentMethodCreateExt},
-        domain,
+        domain, payment_methods as pm_types,
         storage::enums as storage_enums,
     },
     utils::{generate_id, OptionExt},
@@ -92,6 +95,8 @@ pub async fn save_payment_method<FData>(
     business_profile: &domain::Profile,
     mut original_connector_mandate_reference_id: Option<ConnectorMandateReferenceId>,
     merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
+    vault_operation: Option<hyperswitch_domain_models::payments::VaultOperation>,
+    payment_method_info: Option<domain::PaymentMethod>,
 ) -> RouterResult<SavePaymentMethodDataResponse>
 where
     FData: mandate::MandateBehaviour + Clone,
@@ -194,9 +199,11 @@ where
                 };
 
             let pm_id = if customer_acceptance.is_some() {
+                let payment_method_data =
+                    save_payment_method_data.request.get_payment_method_data();
                 let payment_method_create_request =
                     payment_methods::get_payment_method_create_request(
-                        Some(&save_payment_method_data.request.get_payment_method_data()),
+                        Some(&payment_method_data),
                         Some(save_payment_method_data.payment_method),
                         payment_method_type,
                         &customer_id.clone(),
@@ -219,42 +226,22 @@ where
                     .await?;
                     ((res, dc, None), None)
                 } else {
-                    pm_status = Some(common_enums::PaymentMethodStatus::from(
+                    let payment_method_status = common_enums::PaymentMethodStatus::from(
                         save_payment_method_data.attempt_status,
-                    ));
-                    let (res, dc) = Box::pin(save_in_locker(
+                    );
+                    pm_status = Some(payment_method_status);
+                    save_card_and_network_token_in_locker(
                         state,
+                        customer_id.clone(),
+                        payment_method_status,
+                        payment_method_data.clone(),
+                        vault_operation,
+                        payment_method_info,
                         merchant_context,
-                        payment_method_create_request.to_owned(),
-                    ))
-                    .await?;
-
-                    if is_network_tokenization_enabled {
-                        let pm_data = &save_payment_method_data.request.get_payment_method_data();
-                        match pm_data {
-                            domain::PaymentMethodData::Card(card) => {
-                                let (
-                                    network_token_resp,
-                                    _network_token_duplication_check, //the duplication check is discarded, since each card has only one token, handling card duplication check will be suffice
-                                    network_token_requestor_ref_id,
-                                ) = Box::pin(save_network_token_in_locker(
-                                    state,
-                                    merchant_context,
-                                    card,
-                                    payment_method_create_request.clone(),
-                                ))
-                                .await?;
-
-                                (
-                                    (res, dc, network_token_requestor_ref_id),
-                                    network_token_resp,
-                                )
-                            }
-                            _ => ((res, dc, None), None), //network_token_resp is None in case of other payment methods
-                        }
-                    } else {
-                        ((res, dc, None), None)
-                    }
+                        payment_method_create_request.clone(),
+                        is_network_tokenization_enabled,
+                    )
+                    .await?
                 };
                 let network_token_locker_id = match network_token_resp {
                     Some(ref token_resp) => {
@@ -267,10 +254,7 @@ where
                     None => None,
                 };
 
-                let optional_pm_details = match (
-                    resp.card.as_ref(),
-                    save_payment_method_data.request.get_payment_method_data(),
-                ) {
+                let optional_pm_details = match (resp.card.as_ref(), payment_method_data) {
                     (Some(card), _) => Some(PaymentMethodsData::Card(
                         CardDetailsPaymentMethod::from(card.clone()),
                     )),
@@ -840,6 +824,80 @@ where
     todo!()
 }
 
+#[cfg(feature = "v1")]
+pub async fn pre_payment_tokenization(
+    state: &SessionState,
+    customer_id: id_type::CustomerId,
+    card: &payment_method_data::Card,
+) -> RouterResult<(Option<pm_types::TokenResponse>, Option<String>)> {
+    let network_tokenization_supported_card_networks = &state
+        .conf
+        .network_tokenization_supported_card_networks
+        .card_networks;
+
+    if card
+        .card_network
+        .as_ref()
+        .filter(|cn| network_tokenization_supported_card_networks.contains(cn))
+        .is_some()
+    {
+        let optional_card_cvc = Some(card.card_cvc.clone());
+        let card_detail = payment_method_data::CardDetail::from(card);
+        match network_tokenization::make_card_network_tokenization_request(
+            state,
+            &card_detail,
+            optional_card_cvc,
+            &customer_id,
+        )
+        .await
+        {
+            Ok((_token_response, network_token_requestor_ref_id)) => {
+                let network_tokenization_service = &state.conf.network_tokenization_service;
+                match (
+                    network_token_requestor_ref_id.clone(),
+                    network_tokenization_service,
+                ) {
+                    (Some(token_ref), Some(network_tokenization_service)) => {
+                        let network_token = record_operation_time(
+                            async {
+                                network_tokenization::get_network_token(
+                                    state,
+                                    customer_id,
+                                    token_ref,
+                                    network_tokenization_service.get_inner(),
+                                )
+                                .await
+                            },
+                            &metrics::FETCH_NETWORK_TOKEN_TIME,
+                            &[],
+                        )
+                        .await;
+                        match network_token {
+                            Ok(token_response) => {
+                                Ok((Some(token_response), network_token_requestor_ref_id.clone()))
+                            }
+                            _ => {
+                                logger::error!(
+                                    "Error while fetching token from tokenization service"
+                                );
+                                Ok((None, network_token_requestor_ref_id.clone()))
+                            }
+                        }
+                    }
+                    (Some(token_ref), _) => Ok((None, Some(token_ref))),
+                    _ => Ok((None, None)),
+                }
+            }
+            Err(err) => {
+                logger::error!("Failed to tokenize card: {:?}", err);
+                Ok((None, None)) //None will be returned in case of error when calling network tokenization service
+            }
+        }
+    } else {
+        Ok((None, None)) //None will be returned in case of unsupported card network.
+    }
+}
+
 #[cfg(all(
     any(feature = "v1", feature = "v2"),
     not(feature = "payment_methods_v2")
@@ -951,6 +1009,7 @@ pub async fn save_in_locker(
     state: &SessionState,
     merchant_context: &domain::MerchantContext,
     payment_method_request: api::PaymentMethodCreate,
+    card_detail: Option<api::CardDetail>,
 ) -> RouterResult<(
     api_models::payment_methods::PaymentMethodResponse,
     Option<payment_methods::transformers::DataDuplicationCheck>,
@@ -961,8 +1020,8 @@ pub async fn save_in_locker(
         .customer_id
         .clone()
         .get_required_value("customer_id")?;
-    match payment_method_request.card.clone() {
-        Some(card) => Box::pin(
+    match (payment_method_request.card.clone(), card_detail) {
+        (_, Some(card)) | (Some(card), _) => Box::pin(
             PmCards {
                 state,
                 merchant_context,
@@ -972,7 +1031,7 @@ pub async fn save_in_locker(
         .await
         .change_context(errors::ApiErrorResponse::InternalServerError)
         .attach_printable("Add Card Failed"),
-        None => {
+        _ => {
             let pm_id = common_utils::generate_id(consts::ID_LENGTH, "pm");
             let payment_method_response = api::PaymentMethodResponse {
                 merchant_id: merchant_id.clone(),
@@ -1029,7 +1088,8 @@ pub async fn save_network_token_in_locker(
 pub async fn save_network_token_in_locker(
     state: &SessionState,
     merchant_context: &domain::MerchantContext,
-    card_data: &domain::Card,
+    card_data: &payment_method_data::Card,
+    network_token_data: Option<api::CardDetail>,
     payment_method_request: api::PaymentMethodCreate,
 ) -> RouterResult<(
     Option<api_models::payment_methods::PaymentMethodResponse>,
@@ -1045,60 +1105,83 @@ pub async fn save_network_token_in_locker(
         .network_tokenization_supported_card_networks
         .card_networks;
 
-    if card_data
-        .card_network
-        .as_ref()
-        .filter(|cn| network_tokenization_supported_card_networks.contains(cn))
-        .is_some()
-    {
-        let optional_card_cvc = Some(card_data.card_cvc.clone());
-        match network_tokenization::make_card_network_tokenization_request(
-            state,
-            &domain::CardDetail::from(card_data),
-            optional_card_cvc,
-            &customer_id,
-        )
-        .await
-        {
-            Ok((token_response, network_token_requestor_ref_id)) => {
-                // Only proceed if the tokenization was successful
-                let network_token_data = api::CardDetail {
-                    card_number: token_response.token.clone(),
-                    card_exp_month: token_response.token_expiry_month.clone(),
-                    card_exp_year: token_response.token_expiry_year.clone(),
-                    card_holder_name: None,
-                    nick_name: None,
-                    card_issuing_country: None,
-                    card_network: Some(token_response.card_brand.clone()),
-                    card_issuer: None,
-                    card_type: None,
-                };
+    match network_token_data {
+        Some(nt_data) => {
+            let (res, dc) = Box::pin(
+                PmCards {
+                    state,
+                    merchant_context,
+                }
+                .add_card_to_locker(
+                    payment_method_request,
+                    &nt_data,
+                    &customer_id,
+                    None,
+                ),
+            )
+            .await
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("Add Network Token Failed")?;
 
-                let (res, dc) = Box::pin(
-                    PmCards {
-                        state,
-                        merchant_context,
-                    }
-                    .add_card_to_locker(
-                        payment_method_request,
-                        &network_token_data,
-                        &customer_id,
-                        None,
-                    ),
+            Ok((Some(res), dc, None))
+        }
+        None => {
+            if card_data
+                .card_network
+                .as_ref()
+                .filter(|cn| network_tokenization_supported_card_networks.contains(cn))
+                .is_some()
+            {
+                let optional_card_cvc = Some(card_data.card_cvc.clone());
+                match network_tokenization::make_card_network_tokenization_request(
+                    state,
+                    &domain::CardDetail::from(card_data),
+                    optional_card_cvc,
+                    &customer_id,
                 )
                 .await
-                .change_context(errors::ApiErrorResponse::InternalServerError)
-                .attach_printable("Add Network Token Failed")?;
+                {
+                    Ok((token_response, network_token_requestor_ref_id)) => {
+                        // Only proceed if the tokenization was successful
+                        let network_token_data = api::CardDetail {
+                            card_number: token_response.token.clone(),
+                            card_exp_month: token_response.token_expiry_month.clone(),
+                            card_exp_year: token_response.token_expiry_year.clone(),
+                            card_holder_name: None,
+                            nick_name: None,
+                            card_issuing_country: None,
+                            card_network: Some(token_response.card_brand.clone()),
+                            card_issuer: None,
+                            card_type: None,
+                        };
 
-                Ok((Some(res), dc, network_token_requestor_ref_id))
-            }
-            Err(err) => {
-                logger::error!("Failed to tokenize card: {:?}", err);
-                Ok((None, None, None)) //None will be returned in case of error when calling network tokenization service
+                        let (res, dc) = Box::pin(
+                            PmCards {
+                                state,
+                                merchant_context,
+                            }
+                            .add_card_to_locker(
+                                payment_method_request,
+                                &network_token_data,
+                                &customer_id,
+                                None,
+                            ),
+                        )
+                        .await
+                        .change_context(errors::ApiErrorResponse::InternalServerError)
+                        .attach_printable("Add Network Token Failed")?;
+
+                        Ok((Some(res), dc, network_token_requestor_ref_id))
+                    }
+                    Err(err) => {
+                        logger::error!("Failed to tokenize card: {:?}", err);
+                        Ok((None, None, None)) //None will be returned in case of error when calling network tokenization service
+                    }
+                }
+            } else {
+                Ok((None, None, None)) //None will be returned in case of unsupported card network.
             }
         }
-    } else {
-        Ok((None, None, None)) //None will be returned in case of unsupported card network.
     }
 }
 
@@ -1441,3 +1524,171 @@ pub async fn add_token_for_payment_method(
         }),
     }
 }
+
+#[cfg(feature = "v1")]
+#[allow(clippy::too_many_arguments)]
+pub async fn save_card_and_network_token_in_locker(
+    state: &SessionState,
+    customer_id: id_type::CustomerId,
+    payment_method_status: common_enums::PaymentMethodStatus,
+    payment_method_data: domain::PaymentMethodData,
+    vault_operation: Option<hyperswitch_domain_models::payments::VaultOperation>,
+    payment_method_info: Option<domain::PaymentMethod>,
+    merchant_context: &domain::MerchantContext,
+    payment_method_create_request: api::PaymentMethodCreate,
+    is_network_tokenization_enabled: bool,
+) -> RouterResult<(
+    (
+        api_models::payment_methods::PaymentMethodResponse,
+        Option<payment_methods::transformers::DataDuplicationCheck>,
+        Option<String>,
+    ),
+    Option<api_models::payment_methods::PaymentMethodResponse>,
+)> {
+    let network_token_requestor_reference_id = payment_method_info
+        .and_then(|pm_info| pm_info.network_token_requestor_reference_id.clone());
+
+    match vault_operation {
+        Some(hyperswitch_domain_models::payments::VaultOperation::SaveCardData(card)) => {
+            let card_data = api::CardDetail::from(card.card_data.clone());
+            if let (Some(nt_ref_id), Some(tokenization_service)) = (
+                card.network_token_req_ref_id.clone(),
+                &state.conf.network_tokenization_service,
+            ) {
+                let _ = record_operation_time(
+                    async {
+                        network_tokenization::delete_network_token_from_tokenization_service(
+                            state,
+                            nt_ref_id.clone(),
+                            &customer_id,
+                            tokenization_service.get_inner(),
+                        )
+                        .await
+                    },
+                    &metrics::DELETE_NETWORK_TOKEN_TIME,
+                    &[],
+                )
+                .await;
+            }
+            let (res, dc) = Box::pin(save_in_locker(
+                state,
+                merchant_context,
+                payment_method_create_request.to_owned(),
+                Some(card_data),
+            ))
+            .await
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("Add Card In Locker Failed")?;
+
+            Ok(((res, dc, None), None))
+        }
+        Some(hyperswitch_domain_models::payments::VaultOperation::SaveCardAndNetworkTokenData(
+            save_card_and_network_token_data,
+        )) => {
+            let card_data =
+                api::CardDetail::from(save_card_and_network_token_data.card_data.clone());
+
+            let network_token_data = api::CardDetail::from(
+                save_card_and_network_token_data
+                    .network_token
+                    .network_token_data
+                    .clone(),
+            );
+
+            if payment_method_status == common_enums::PaymentMethodStatus::Active {
+                let (res, dc) = Box::pin(save_in_locker(
+                    state,
+                    merchant_context,
+                    payment_method_create_request.to_owned(),
+                    Some(card_data),
+                ))
+                .await
+                .change_context(errors::ApiErrorResponse::InternalServerError)
+                .attach_printable("Add Card In Locker Failed")?;
+
+                let (network_token_resp, _dc, _) = Box::pin(save_network_token_in_locker(
+                    state,
+                    merchant_context,
+                    &save_card_and_network_token_data.card_data,
+                    Some(network_token_data),
+                    payment_method_create_request.clone(),
+                ))
+                .await
+                .change_context(errors::ApiErrorResponse::InternalServerError)
+                .attach_printable("Add Network Token In Locker Failed")?;
+
+                Ok((
+                    (res, dc, network_token_requestor_reference_id),
+                    network_token_resp,
+                ))
+            } else {
+                if let (Some(nt_ref_id), Some(tokenization_service)) = (
+                    network_token_requestor_reference_id.clone(),
+                    &state.conf.network_tokenization_service,
+                ) {
+                    let _ = record_operation_time(
+                        async {
+                            network_tokenization::delete_network_token_from_tokenization_service(
+                                state,
+                                nt_ref_id.clone(),
+                                &customer_id,
+                                tokenization_service.get_inner(),
+                            )
+                            .await
+                        },
+                        &metrics::DELETE_NETWORK_TOKEN_TIME,
+                        &[],
+                    )
+                    .await;
+                }
+                let (res, dc) = Box::pin(save_in_locker(
+                    state,
+                    merchant_context,
+                    payment_method_create_request.to_owned(),
+                    Some(card_data),
+                ))
+                .await
+                .change_context(errors::ApiErrorResponse::InternalServerError)
+                .attach_printable("Add Card In Locker Failed")?;
+
+                Ok(((res, dc, None), None))
+            }
+        }
+        _ => {
+            let (res, dc) = Box::pin(save_in_locker(
+                state,
+                merchant_context,
+                payment_method_create_request.to_owned(),
+                None,
+            ))
+            .await?;
+
+            if is_network_tokenization_enabled {
+                match &payment_method_data {
+                    domain::PaymentMethodData::Card(card) => {
+                        let (
+                            network_token_resp,
+                            _network_token_duplication_check, //the duplication check is discarded, since each card has only one token, handling card duplication check will be suffice
+                            network_token_requestor_ref_id,
+                        ) = Box::pin(save_network_token_in_locker(
+                            state,
+                            merchant_context,
+                            card,
+                            None,
+                            payment_method_create_request.clone(),
+                        ))
+                        .await?;
+
+                        Ok((
+                            (res, dc, network_token_requestor_ref_id),
+                            network_token_resp,
+                        ))
+                    }
+                    _ => Ok(((res, dc, None), None)), //network_token_resp is None in case of other payment methods
+                }
+            } else {
+                Ok(((res, dc, None), None))
+            }
+        }
+    }
+}
diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs
index 7e715d40ce6..95aca373080 100644
--- a/crates/router/src/core/payments/transformers.rs
+++ b/crates/router/src/core/payments/transformers.rs
@@ -40,7 +40,7 @@ use crate::{
     types::{
         self,
         api::{self, ConnectorTransactionId},
-        domain,
+        domain, payment_methods as pm_types,
         storage::{self, enums},
         transformers::{ForeignFrom, ForeignInto, ForeignTryFrom},
         MultipleCaptureRequestData,
@@ -5159,3 +5159,22 @@ impl ForeignFrom<(Self, Option<&api_models::payments::AdditionalPaymentData>)>
             })
     }
 }
+
+#[cfg(feature = "v1")]
+impl From<pm_types::TokenResponse> for domain::NetworkTokenData {
+    fn from(token_response: pm_types::TokenResponse) -> Self {
+        Self {
+            token_number: token_response.authentication_details.token,
+            token_exp_month: token_response.token_details.exp_month,
+            token_exp_year: token_response.token_details.exp_year,
+            token_cryptogram: Some(token_response.authentication_details.cryptogram),
+            card_issuer: None,
+            card_network: Some(token_response.network),
+            card_type: None,
+            card_issuing_country: None,
+            bank_code: None,
+            nick_name: None,
+            eci: None,
+        }
+    }
+}
diff --git a/crates/router/src/types/api/admin.rs b/crates/router/src/types/api/admin.rs
index bf7596adb43..50bd54756d1 100644
--- a/crates/router/src/types/api/admin.rs
+++ b/crates/router/src/types/api/admin.rs
@@ -195,6 +195,7 @@ impl ForeignTryFrom<domain::Profile> for ProfileResponse {
             force_3ds_challenge: item.force_3ds_challenge,
             is_debit_routing_enabled: Some(item.is_debit_routing_enabled),
             merchant_business_country: item.merchant_business_country,
+            is_pre_network_tokenization_enabled: item.is_pre_network_tokenization_enabled,
         })
     }
 }
@@ -443,5 +444,8 @@ pub async fn create_profile_from_merchant_account(
         is_debit_routing_enabled: request.is_debit_routing_enabled.unwrap_or_default(),
         merchant_business_country: request.merchant_business_country,
         is_iframe_redirection_enabled: request.is_iframe_redirection_enabled,
+        is_pre_network_tokenization_enabled: request
+            .is_pre_network_tokenization_enabled
+            .unwrap_or_default(),
     }))
 }
diff --git a/migrations/2025-05-16-064616_add_is_pre_network_tokenization_enabled_in_business_profile/down.sql b/migrations/2025-05-16-064616_add_is_pre_network_tokenization_enabled_in_business_profile/down.sql
new file mode 100644
index 00000000000..6d40bb55733
--- /dev/null
+++ b/migrations/2025-05-16-064616_add_is_pre_network_tokenization_enabled_in_business_profile/down.sql
@@ -0,0 +1,2 @@
+-- This file should undo anything in `up.sql`
+ALTER TABLE business_profile DROP COLUMN IF EXISTS is_pre_network_tokenization_enabled;
\ No newline at end of file
diff --git a/migrations/2025-05-16-064616_add_is_pre_network_tokenization_enabled_in_business_profile/up.sql b/migrations/2025-05-16-064616_add_is_pre_network_tokenization_enabled_in_business_profile/up.sql
new file mode 100644
index 00000000000..23448365f35
--- /dev/null
+++ b/migrations/2025-05-16-064616_add_is_pre_network_tokenization_enabled_in_business_profile/up.sql
@@ -0,0 +1,2 @@
+-- Your SQL goes here
+ALTER TABLE business_profile ADD COLUMN IF NOT EXISTS is_pre_network_tokenization_enabled BOOLEAN;
\ No newline at end of file
diff --git a/v2_migrations/2025-05-05-073505_remove_is_pre_network_tokenization_enabled_from_business_profile/down.sql b/v2_migrations/2025-05-05-073505_remove_is_pre_network_tokenization_enabled_from_business_profile/down.sql
new file mode 100644
index 00000000000..d239f77f1a5
--- /dev/null
+++ b/v2_migrations/2025-05-05-073505_remove_is_pre_network_tokenization_enabled_from_business_profile/down.sql
@@ -0,0 +1,2 @@
+-- This file should undo anything in `up.sql`
+ALTER TABLE business_profile ADD COLUMN IF NOT EXISTS is_pre_network_tokenization_enabled BOOLEAN;
\ No newline at end of file
diff --git a/v2_migrations/2025-05-05-073505_remove_is_pre_network_tokenization_enabled_from_business_profile/up.sql b/v2_migrations/2025-05-05-073505_remove_is_pre_network_tokenization_enabled_from_business_profile/up.sql
new file mode 100644
index 00000000000..68100350105
--- /dev/null
+++ b/v2_migrations/2025-05-05-073505_remove_is_pre_network_tokenization_enabled_from_business_profile/up.sql
@@ -0,0 +1,2 @@
+-- Your SQL goes here
+ALTER TABLE business_profile DROP COLUMN IF EXISTS is_pre_network_tokenization_enabled;
\ No newline at end of file

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

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

## Description
<!-- Describe your changes in detail -->
Introduced a new field in business_profile (is_pre_network_tokenization_enabled).
Based on that field the card will be tokenized before first payment.
If the payment get succeeded the card and network token data will be stored in the locker afterwards.


### Additional Changes

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

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

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


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

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


## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->
1. Enable is_network_tokenization_enabled and is_pre_network_tokenization_enabled in business_profile
2. Create a card payment with a network tokenization supported connector. (Customer acceptance should be there)

Connector Create(Cybersource):
```
curl --location 'http://localhost:8080/account/merchant_1747299015/connectors' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_6YZ1hGvUYrgBzKrKO25WteTts6KvLVXCodhEmuqaogJZvLhGIEVSVNftVcZytpdE' \
--data '{
    "connector_type": "payment_processor",
    "connector_name": "cybersource",
    "connector_account_details": {
        "auth_type": "SignatureKey",
        "api_secret": "abc",
        "api_key": "abc",
        "key1": "abc"
    },
    "test_mode": true,
    "disabled": false,
    "payment_methods_enabled": [
        {
            "payment_method": "wallet",
            "payment_method_types": [
                {
                    "payment_method_type": "google_pay",
                    "payment_experience": "invoke_sdk_client",
                    "card_networks": null,
                    "accepted_currencies": null,
                    "accepted_countries": null,
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                }
            ]
        },
        {
            "payment_method": "card",
            "payment_method_types": [
                {
                    "payment_method_type": "credit",
                    "payment_experience": null,
                    "card_networks": [
                        "Visa",
                        "Mastercard",
                        "AmericanExpress"
                    ],
                    "accepted_currencies": null,
                    "accepted_countries": null,
                    "minimum_amount": -1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                },
                {
                    "payment_method_type": "debit",
                    "payment_experience": null,
                    "card_networks": [
                        "Visa",
                        "Mastercard"
                    ],
                    "accepted_currencies": null,
                    "accepted_countries": null,
                    "minimum_amount": -1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                }
            ]
        }
    ],
    "metadata": {
        "status_url": "url",
        "account_name": "transaction_processing",
        "pricing_type": "fixed_price",
        "acquirer_bin": "438309",
        "acquirer_merchant_id": "00002000000"
    },
    "business_country": "US",
    "business_label": "default",
    "frm_configs": null,
    "connector_webhook_details": {
        "merchant_secret": "abc"
    }
}'
```

Payments Create:
```
curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data-raw '{
    "amount": 10,
    "amount_to_capture": 10,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "customer_id": "customer1747403711",
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+1",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "setup_future_usage": "on_session",
    "return_url": "http://127.0.0.1:4040",
    "customer_acceptance": {
        "acceptance_type": "offline",
        "accepted_at": "1963-05-03T04:07:52.723Z",
        "online": {
            "ip_address": "in sit",
            "user_agent": "amet irure esse"
        }
    },
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "card_number": "4761360080000093",
            "card_exp_month": "01",
            "card_exp_year": "2026",
            "card_holder_name": "joseph Doe",
            "card_cvc": "947"
        }
    },
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US"
        },
        "email": "soppa@gmail.com"
    },
    "browser_info": {
        "user_agent": "Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/70.0.3538.110 Safari\/537.36",
        "accept_header": "text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/webp,image\/apng,*\/*;q=0.8",
        "language": "nl-NL",
        "color_depth": 24,
        "screen_height": 723,
        "screen_width": 1536,
        "time_zone": 0,
        "java_enabled": true,
        "java_script_enabled": true,
        "ip_address": "125.0.0.1"
    },
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
    
}'
```
Response:
```
{
    "payment_id": "pay_hW2g73Y3LCCl8K59On4s",
    "merchant_id": "merchant_1747404722",
    "status": "succeeded",
    "amount": 10,
    "net_amount": 10,
    "shipping_cost": null,
    "amount_capturable": 0,
    "amount_received": 10,
    "connector": "cybersource",
    "client_secret": "abc",
    "created": "2025-05-16T14:13:37.029Z",
    "currency": "USD",
    "customer_id": "customer1747404817",
    "customer": {
        "id": "customer1747404817",
        "name": "John Doe",
        "email": "guest@example.com",
        "phone": "999999999",
        "phone_country_code": "+1"
    },
    "description": "Its my first payment request",
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": "on_session",
    "off_session": null,
    "capture_on": null,
    "capture_method": "automatic",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "last4": "0093",
            "card_type": "DEBIT",
            "card_network": "Visa",
            "card_issuer": "BANKMUSCAT (SAOG)",
            "card_issuing_country": "OMAN",
            "card_isin": "476136",
            "card_extended_bin": null,
            "card_exp_month": "01",
            "card_exp_year": "2026",
            "card_holder_name": "joseph Doe",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": null,
    "billing": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": null,
            "last_name": null
        },
        "phone": null,
        "email": "soppa@gmail.com"
    },
    "order_details": null,
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "return_url": "http://127.0.0.1:4040/",
    "authentication_type": "no_three_ds",
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "next_action": null,
    "cancellation_reason": null,
    "error_code": null,
    "error_message": null,
    "unified_code": null,
    "unified_message": null,
    "payment_experience": null,
    "payment_method_type": "debit",
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": {
        "customer_id": "customer1747404817",
        "created_at": 1747404816,
        "expires": 1747408416,
        "secret": "abc"
    },
    "manual_retry_allowed": false,
    "connector_transaction_id": "abc",
    "frm_message": null,
    "metadata": {
        "udf1": "value1",
        "login_date": "2019-09-10T10:11:12Z",
        "new_customer": "true"
    },
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": "pay_hW2g73Y3LCCl8K59On4s_1",
    "payment_link": null,
    "profile_id": "pro_cjkyoVtfdaQY5JKvfh3v",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_WI36rV75QJhAyHC02iup",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2025-05-16T14:28:37.028Z",
    "fingerprint": null,
    "browser_info": {
        "language": "nl-NL",
        "time_zone": 0,
        "ip_address": "125.0.0.1",
        "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
        "color_depth": 24,
        "java_enabled": true,
        "screen_width": 1536,
        "accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "screen_height": 723,
        "java_script_enabled": true
    },
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2025-05-16T14:13:44.489Z",
    "split_payments": null,
    "frm_metadata": null,
    "extended_authorization_applied": null,
    "capture_before": null,
    "merchant_order_reference_id": null,
    "order_tax_amount": null,
    "connector_mandate_id": null,
    "card_discovery": "manual",
    "force_3ds_challenge": false,
    "force_3ds_challenge_trigger": false,
    "issuer_error_code": null,
    "issuer_error_message": null,
    "is_iframe_redirection_enabled": null
}
```

**Logs:**
Raw Connector Request:
```
raw_connector_request: Object {"processingInformation": Object {"actionList": Null, "actionTokenTypes": Null, "authorizationOptions": Object {"initiator": Null, "merchantIntitiatedTransaction": Null, "ignoreAvsResult": Null, "ignoreCvResult": Null}, "commerceIndicator": String("internet"), "capture": Bool(true), "captureOptions": Null, "paymentSolution": Null}, "paymentInformation": Object {"tokenizedCard": Object {"number": String("460400**********"), "expirationMonth": String("*** alloc::string::String ***"), "expirationYear": String("*** alloc::string::String ***"), "cryptogram": String("*** alloc::string::String ***"), "transactionType": String("1")}}, "orderInformation": Object {"amountDetails": Object {"totalAmount": String("0.10"), "currency": String("USD")}, "billTo": Object {"firstName": String("*** alloc::string::String ***"), "lastName": String("*** alloc::string::String ***"), "address1": String("*** alloc::string::String ***"), "locality": String("San Fransico"), "administrativeArea": String("*** alloc::string::String ***"), "postalCode": String("*** alloc::string::String ***"), "country": String("US"), "email": String("*****@example.com")}}, "clientReferenceInformation": Object {"code": String("pay_hW2g73Y3LCCl8K59On4s_1")}, "merchantDefinedInformation": Array [Object {"key": Number(1), "value": String("login_date=\"2019-09-10T10:11:12Z\"")}, Object {"key": Number(2), "value": String("new_customer=\"true\"")}, Object {"key": Number(3), "value": String("udf1=\"value1\"")}]}
```


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