Bug: [Refactor]: Make use of Locker for generating/storing fingerprints



# Description
Previously we were generating the fingerprints on our application end itself after this change we will have the fingerprints generated by our locker.
Fingerprint is generated for each and every card payment. Moreover we are storing fingerprint in all the attempt tables.
Now vouching for the scenarios were there are more than one attempt associated with one intent in that case we are adding the successful attempt's fingerprint in intent.

So how does the whole flow works:
1. Before confirmation of payment the payment instrument's fingerprint is cross-checked with the blocklist.
2. If it is present in blocklist the payment will be blocked (status as false).
3. This payment can be retried with another instrument and in case if succeeds the fingerprint will be added in intent.

**Note**: All successful attempts will have a fingerprint but intent's fingerprint will always be the successful attempt's fingerprint.

Other checkpoints under same hood:-
- [x] Instead of generating the fingerprint in application add support for `/cards/fingerprint` API call with `hash_key`  (Also ensure to whitelist this endpoint in proxy).
- [x] Storing the fingerprint in payment attempt and payment intent table.
- [x] Refactoring Block List checking using the fingerprint API

# Testing

### Generating fingerprints
-> Toggle the blocklist guard from merchant account being used using `/blocklist/toggle?status=true`. More about 
     toggling guard [here](https://github.com/juspay/hyperswitch/issues/3467).
-> We need to create a payment.
-> While trying to confirm the payment it will have the `fingerprint_id` in the response. This can be used to block the 
     instrument. If the payment was able to be captured the fingerprint will be stored in the intent table as well

### Blocking fingerprints
Refer to the attached postman collection for the API contracts for the blocklist APIs([Description](https://github.com/juspay/hyperswitch/issues/3439)). Currently we support blocking three types of resources i.e. card numbers (payment intrument), card bin, and extended card bin.
[blocklist_api_postman.zip](https://github.com/juspay/hyperswitch/files/13893696/blocklist_api_postman.zip)

```
For Card Bin and Extended Card Bin :-
1. Setup a Merchant Account and any Connector account
2. Make a payment with a certain card (ensure it succeeds)
3. Block the card's card bin or extended card bin
4. Try the payment again (should fail this time with an API response saying that the payment was blocked)

For Payment Instrument :-
1. Repeat steps 1 and 2 of previous section
2. In the payment confirm response, there will be an additional field called "fingerprint". This is the fingerprint id that can be used to block a particular payment method. Use this to block the card.
3. Try the payment again (should fail)
```

# Curls for testing out the complete flow
### Toggle Blocklist Guard for merchant
```
Req

curl --location --request POST 'https://sandbox.hyperswitch.io/blocklist/toggle?status=true' \
--header 'api-key: snd_key'

Response

{
    "blocklist_guard_status": "enabled"
}
```

### Blocklisting fingerprint for merchant
```
Req

curl --location 'https://sandbox.hyperswitch.io/blocklist' \
--header 'x-feature: router-custom' \
--header 'Content-Type: application/json' \
--header 'api-key:snd_key' \
--data '{
    "type": "fingerprint",
    "data": "**fingerprint got in intent**"
}
'

Response

{
    "fingerprint_id": "**fingerprint got in intent**",
    "data_kind": "payment_method",
    "created_at": "2024-02-21T07:47:01.939Z"
}
```

### Listing Blocked fingerprints of merchant
```
Req

curl --location 'https://sandbox.hyperswitch.io/blocklist?data_kind=payment_method' \
--header 'x-feature: router-custom' \
--header 'api-key: snd_key'
'

Response

[
{
    "fingerprint_id": "**fingerprint got in intent**",
    "data_kind": "payment_method",
    "created_at": "2024-02-21T07:47:01.939Z"
}
]
```

### Unblocking Blocked fingerprints for merchant
```
Req

curl --location --request DELETE 'https://sandbox.hyperswitch.io/blocklist' \
--header 'x-feature: router-custom' \
--header 'Content-Type: application/json' \
--header 'api-key: snd_key' \
--data '{
    "type": "fingerprint",
    "data": "**fingerprint got in list blocklist**"
}'

Response

[
{
    "fingerprint_id": "**fingerprint got in list blocklist**",
    "data_kind": "payment_method",
    "created_at": "2024-02-21T07:47:01.939Z"
}
]
```

diff --git a/crates/api_models/src/blocklist.rs b/crates/api_models/src/blocklist.rs
index 9672b6de6ee..2afeb2db4f6 100644
--- a/crates/api_models/src/blocklist.rs
+++ b/crates/api_models/src/blocklist.rs
@@ -1,5 +1,6 @@
 use common_enums::enums;
 use common_utils::events::ApiEventMetric;
+use masking::StrongSecret;
 use utoipa::ToSchema;
 
 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
@@ -10,6 +11,15 @@ pub enum BlocklistRequest {
     ExtendedCardBin(String),
 }
 
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
+pub struct GenerateFingerprintRequest {
+    pub card: Card,
+    pub hash_key: StrongSecret<String>,
+}
+#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
+pub struct Card {
+    pub card_number: StrongSecret<String>,
+}
 pub type AddToBlocklistRequest = BlocklistRequest;
 pub type DeleteFromBlocklistRequest = BlocklistRequest;
 
@@ -22,6 +32,11 @@ pub struct BlocklistResponse {
     pub created_at: time::PrimitiveDateTime,
 }
 
+#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[serde(rename_all = "snake_case")]
+pub struct GenerateFingerprintResponsePayload {
+    pub card_fingerprint: String,
+}
 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
 pub struct ToggleBlocklistResponse {
     pub blocklist_guard_status: String,
@@ -54,4 +69,7 @@ impl ApiEventMetric for BlocklistRequest {}
 impl ApiEventMetric for BlocklistResponse {}
 impl ApiEventMetric for ToggleBlocklistResponse {}
 impl ApiEventMetric for ListBlocklistQuery {}
+impl ApiEventMetric for GenerateFingerprintRequest {}
 impl ApiEventMetric for ToggleBlocklistQuery {}
+impl ApiEventMetric for GenerateFingerprintResponsePayload {}
+impl ApiEventMetric for Card {}
diff --git a/crates/data_models/src/payments/payment_attempt.rs b/crates/data_models/src/payments/payment_attempt.rs
index 084b0ef251e..3e187e25bc5 100644
--- a/crates/data_models/src/payments/payment_attempt.rs
+++ b/crates/data_models/src/payments/payment_attempt.rs
@@ -160,6 +160,7 @@ pub struct PaymentAttempt {
     pub unified_code: Option<String>,
     pub unified_message: Option<String>,
     pub mandate_data: Option<MandateDetails>,
+    pub fingerprint_id: Option<String>,
 }
 
 impl PaymentAttempt {
@@ -238,6 +239,7 @@ pub struct PaymentAttemptNew {
     pub unified_code: Option<String>,
     pub unified_message: Option<String>,
     pub mandate_data: Option<MandateDetails>,
+    pub fingerprint_id: Option<String>,
 }
 
 impl PaymentAttemptNew {
@@ -270,6 +272,7 @@ pub enum PaymentAttemptUpdate {
         capture_method: Option<storage_enums::CaptureMethod>,
         surcharge_amount: Option<i64>,
         tax_amount: Option<i64>,
+        fingerprint_id: Option<String>,
         updated_by: String,
     },
     UpdateTrackers {
@@ -307,6 +310,7 @@ pub enum PaymentAttemptUpdate {
         surcharge_amount: Option<i64>,
         tax_amount: Option<i64>,
         merchant_connector_id: Option<String>,
+        fingerprint_id: Option<String>,
     },
     RejectUpdate {
         status: storage_enums::AttemptStatus,
diff --git a/crates/data_models/src/payments/payment_intent.rs b/crates/data_models/src/payments/payment_intent.rs
index 7470b5f8502..b2fafeb92a0 100644
--- a/crates/data_models/src/payments/payment_intent.rs
+++ b/crates/data_models/src/payments/payment_intent.rs
@@ -121,6 +121,7 @@ pub enum PaymentIntentUpdate {
         amount_captured: Option<i64>,
         return_url: Option<String>,
         updated_by: String,
+        fingerprint_id: Option<String>,
         incremental_authorization_allowed: Option<bool>,
     },
     MetadataUpdate {
@@ -335,6 +336,7 @@ impl From<PaymentIntentUpdate> for PaymentIntentUpdateInternal {
                 // currency,
                 status,
                 amount_captured,
+                fingerprint_id,
                 // customer_id,
                 return_url,
                 updated_by,
@@ -344,6 +346,7 @@ impl From<PaymentIntentUpdate> for PaymentIntentUpdateInternal {
                 // currency: Some(currency),
                 status: Some(status),
                 amount_captured,
+                fingerprint_id,
                 // customer_id,
                 return_url,
                 modified_at: Some(common_utils::date_time::now()),
diff --git a/crates/diesel_models/src/payment_attempt.rs b/crates/diesel_models/src/payment_attempt.rs
index 9af4595c9f4..927a05bfa56 100644
--- a/crates/diesel_models/src/payment_attempt.rs
+++ b/crates/diesel_models/src/payment_attempt.rs
@@ -65,6 +65,7 @@ pub struct PaymentAttempt {
     pub unified_message: Option<String>,
     pub net_amount: Option<i64>,
     pub mandate_data: Option<storage_enums::MandateDetails>,
+    pub fingerprint_id: Option<String>,
 }
 
 impl PaymentAttempt {
@@ -140,6 +141,7 @@ pub struct PaymentAttemptNew {
     pub unified_message: Option<String>,
     pub net_amount: Option<i64>,
     pub mandate_data: Option<storage_enums::MandateDetails>,
+    pub fingerprint_id: Option<String>,
 }
 
 impl PaymentAttemptNew {
@@ -177,6 +179,7 @@ pub enum PaymentAttemptUpdate {
         capture_method: Option<storage_enums::CaptureMethod>,
         surcharge_amount: Option<i64>,
         tax_amount: Option<i64>,
+        fingerprint_id: Option<String>,
         updated_by: String,
     },
     UpdateTrackers {
@@ -212,6 +215,7 @@ pub enum PaymentAttemptUpdate {
         amount_capturable: Option<i64>,
         surcharge_amount: Option<i64>,
         tax_amount: Option<i64>,
+        fingerprint_id: Option<String>,
         updated_by: String,
         merchant_connector_id: Option<String>,
     },
@@ -351,6 +355,7 @@ pub struct PaymentAttemptUpdateInternal {
     encoded_data: Option<String>,
     unified_code: Option<Option<String>>,
     unified_message: Option<Option<String>>,
+    fingerprint_id: Option<String>,
 }
 
 impl PaymentAttemptUpdateInternal {
@@ -411,6 +416,7 @@ impl PaymentAttemptUpdate {
             encoded_data,
             unified_code,
             unified_message,
+            fingerprint_id,
         } = PaymentAttemptUpdateInternal::from(self).populate_derived_fields(&source);
         PaymentAttempt {
             amount: amount.unwrap_or(source.amount),
@@ -452,6 +458,7 @@ impl PaymentAttemptUpdate {
             encoded_data: encoded_data.or(source.encoded_data),
             unified_code: unified_code.unwrap_or(source.unified_code),
             unified_message: unified_message.unwrap_or(source.unified_message),
+            fingerprint_id: fingerprint_id.or(source.fingerprint_id),
             ..source
         }
     }
@@ -476,6 +483,7 @@ impl From<PaymentAttemptUpdate> for PaymentAttemptUpdateInternal {
                 capture_method,
                 surcharge_amount,
                 tax_amount,
+                fingerprint_id,
                 updated_by,
             } => Self {
                 amount: Some(amount),
@@ -494,6 +502,7 @@ impl From<PaymentAttemptUpdate> for PaymentAttemptUpdateInternal {
                 capture_method,
                 surcharge_amount,
                 tax_amount,
+                fingerprint_id,
                 updated_by,
                 ..Default::default()
             },
@@ -527,6 +536,7 @@ impl From<PaymentAttemptUpdate> for PaymentAttemptUpdateInternal {
                 merchant_connector_id,
                 surcharge_amount,
                 tax_amount,
+                fingerprint_id,
             } => Self {
                 amount: Some(amount),
                 currency: Some(currency),
@@ -549,6 +559,7 @@ impl From<PaymentAttemptUpdate> for PaymentAttemptUpdateInternal {
                 merchant_connector_id: merchant_connector_id.map(Some),
                 surcharge_amount,
                 tax_amount,
+                fingerprint_id,
                 ..Default::default()
             },
             PaymentAttemptUpdate::VoidUpdate {
diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs
index 31bc0c06c51..69fb61a8d42 100644
--- a/crates/diesel_models/src/payment_intent.rs
+++ b/crates/diesel_models/src/payment_intent.rs
@@ -116,6 +116,7 @@ pub enum PaymentIntentUpdate {
     ResponseUpdate {
         status: storage_enums::IntentStatus,
         amount_captured: Option<i64>,
+        fingerprint_id: Option<String>,
         return_url: Option<String>,
         updated_by: String,
         incremental_authorization_allowed: Option<bool>,
@@ -405,6 +406,7 @@ impl From<PaymentIntentUpdate> for PaymentIntentUpdateInternal {
                 // currency,
                 status,
                 amount_captured,
+                fingerprint_id,
                 // customer_id,
                 return_url,
                 updated_by,
@@ -414,6 +416,7 @@ impl From<PaymentIntentUpdate> for PaymentIntentUpdateInternal {
                 // currency: Some(currency),
                 status: Some(status),
                 amount_captured,
+                fingerprint_id,
                 // customer_id,
                 return_url,
                 modified_at: Some(common_utils::date_time::now()),
diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs
index 2cbcb52fb44..5093f0df7d0 100644
--- a/crates/diesel_models/src/schema.rs
+++ b/crates/diesel_models/src/schema.rs
@@ -689,6 +689,8 @@ diesel::table! {
         unified_message -> Nullable<Varchar>,
         net_amount -> Nullable<Int8>,
         mandate_data -> Nullable<Jsonb>,
+        #[max_length = 64]
+        fingerprint_id -> Nullable<Varchar>,
     }
 }
 
diff --git a/crates/diesel_models/src/user/sample_data.rs b/crates/diesel_models/src/user/sample_data.rs
index 6a6d4c52c97..beac5b0733e 100644
--- a/crates/diesel_models/src/user/sample_data.rs
+++ b/crates/diesel_models/src/user/sample_data.rs
@@ -68,6 +68,7 @@ pub struct PaymentAttemptBatchNew {
     pub unified_message: Option<String>,
     pub net_amount: Option<i64>,
     pub mandate_data: Option<MandateDetails>,
+    pub fingerprint_id: Option<String>,
 }
 
 #[allow(dead_code)]
@@ -122,6 +123,7 @@ impl PaymentAttemptBatchNew {
             unified_message: self.unified_message,
             net_amount: self.net_amount,
             mandate_data: self.mandate_data,
+            fingerprint_id: self.fingerprint_id,
         }
     }
 }
diff --git a/crates/router/src/core/blocklist/transformers.rs b/crates/router/src/core/blocklist/transformers.rs
index 2cb5f86a264..f4122a6a651 100644
--- a/crates/router/src/core/blocklist/transformers.rs
+++ b/crates/router/src/core/blocklist/transformers.rs
@@ -1,6 +1,28 @@
-use api_models::blocklist;
+use api_models::{blocklist, enums as api_enums};
+use common_utils::{
+    ext_traits::{Encode, StringExt},
+    request::RequestContent,
+};
+use error_stack::ResultExt;
+use josekit::jwe;
+#[cfg(feature = "aws_kms")]
+use masking::PeekInterface;
+use masking::StrongSecret;
+use router_env::{instrument, tracing};
 
-use crate::types::{storage, transformers::ForeignFrom};
+use crate::{
+    configs::settings,
+    core::{
+        errors::{self, CustomResult},
+        payment_methods::transformers as payment_methods,
+    },
+    headers, routes,
+    services::{api as services, encryption},
+    types::{storage, transformers::ForeignFrom},
+    utils::ConnectorResponseExt,
+};
+
+const LOCKER_FINGERPRINT_PATH: &str = "/cards/fingerprint";
 
 impl ForeignFrom<storage::Blocklist> for blocklist::AddToBlocklistResponse {
     fn foreign_from(from: storage::Blocklist) -> Self {
@@ -11,3 +33,192 @@ impl ForeignFrom<storage::Blocklist> for blocklist::AddToBlocklistResponse {
         }
     }
 }
+
+async fn generate_fingerprint_request<'a>(
+    #[cfg(not(feature = "aws_kms"))] jwekey: &settings::Jwekey,
+    #[cfg(feature = "aws_kms")] jwekey: &settings::ActiveKmsSecrets,
+    locker: &settings::Locker,
+    payload: &blocklist::GenerateFingerprintRequest,
+    locker_choice: api_enums::LockerChoice,
+) -> CustomResult<services::Request, errors::VaultError> {
+    let payload = payload
+        .encode_to_vec()
+        .change_context(errors::VaultError::RequestEncodingFailed)?;
+
+    #[cfg(feature = "aws_kms")]
+    let private_key = jwekey.jwekey.peek().vault_private_key.as_bytes();
+
+    #[cfg(not(feature = "aws_kms"))]
+    let private_key = jwekey.vault_private_key.as_bytes();
+
+    let jws = encryption::jws_sign_payload(&payload, &locker.locker_signing_key_id, private_key)
+        .await
+        .change_context(errors::VaultError::RequestEncodingFailed)?;
+
+    let jwe_payload = generate_jwe_payload_for_request(jwekey, &jws, locker_choice).await?;
+    let mut url = match locker_choice {
+        api_enums::LockerChoice::HyperswitchCardVault => locker.host.to_owned(),
+    };
+    url.push_str(LOCKER_FINGERPRINT_PATH);
+    let mut request = services::Request::new(services::Method::Post, &url);
+    request.add_header(headers::CONTENT_TYPE, "application/json".into());
+    request.set_body(RequestContent::Json(Box::new(jwe_payload)));
+    Ok(request)
+}
+
+async fn generate_jwe_payload_for_request(
+    #[cfg(feature = "aws_kms")] jwekey: &settings::ActiveKmsSecrets,
+    #[cfg(not(feature = "aws_kms"))] jwekey: &settings::Jwekey,
+    jws: &str,
+    locker_choice: api_enums::LockerChoice,
+) -> CustomResult<encryption::JweBody, errors::VaultError> {
+    let jws_payload: Vec<&str> = jws.split('.').collect();
+
+    let generate_jws_body = |payload: Vec<&str>| -> Option<encryption::JwsBody> {
+        Some(encryption::JwsBody {
+            header: payload.first()?.to_string(),
+            payload: payload.get(1)?.to_string(),
+            signature: payload.get(2)?.to_string(),
+        })
+    };
+
+    let jws_body =
+        generate_jws_body(jws_payload).ok_or(errors::VaultError::GenerateFingerprintFailed)?;
+
+    let payload = jws_body
+        .encode_to_vec()
+        .change_context(errors::VaultError::GenerateFingerprintFailed)?;
+
+    #[cfg(feature = "aws_kms")]
+    let public_key = match locker_choice {
+        api_enums::LockerChoice::HyperswitchCardVault => {
+            jwekey.jwekey.peek().vault_encryption_key.as_bytes()
+        }
+    };
+
+    #[cfg(not(feature = "aws_kms"))]
+    let public_key = match locker_choice {
+        api_enums::LockerChoice::HyperswitchCardVault => jwekey.vault_encryption_key.as_bytes(),
+    };
+
+    let jwe_encrypted = encryption::encrypt_jwe(&payload, public_key)
+        .await
+        .change_context(errors::VaultError::SaveCardFailed)
+        .attach_printable("Error on jwe encrypt")?;
+    let jwe_payload: Vec<&str> = jwe_encrypted.split('.').collect();
+
+    let generate_jwe_body = |payload: Vec<&str>| -> Option<encryption::JweBody> {
+        Some(encryption::JweBody {
+            header: payload.first()?.to_string(),
+            iv: payload.get(2)?.to_string(),
+            encrypted_payload: payload.get(3)?.to_string(),
+            tag: payload.get(4)?.to_string(),
+            encrypted_key: payload.get(1)?.to_string(),
+        })
+    };
+
+    let jwe_body =
+        generate_jwe_body(jwe_payload).ok_or(errors::VaultError::GenerateFingerprintFailed)?;
+
+    Ok(jwe_body)
+}
+
+#[instrument(skip_all)]
+pub async fn generate_fingerprint(
+    state: &routes::AppState,
+    card_number: StrongSecret<String>,
+    hash_key: StrongSecret<String>,
+    locker_choice: api_enums::LockerChoice,
+) -> CustomResult<blocklist::GenerateFingerprintResponsePayload, errors::VaultError> {
+    let payload = blocklist::GenerateFingerprintRequest {
+        card: blocklist::Card { card_number },
+        hash_key,
+    };
+
+    let generate_fingerprint_resp =
+        call_to_locker_for_fingerprint(state, &payload, locker_choice).await?;
+
+    Ok(generate_fingerprint_resp)
+}
+
+#[instrument(skip_all)]
+async fn call_to_locker_for_fingerprint(
+    state: &routes::AppState,
+    payload: &blocklist::GenerateFingerprintRequest,
+    locker_choice: api_enums::LockerChoice,
+) -> CustomResult<blocklist::GenerateFingerprintResponsePayload, errors::VaultError> {
+    let locker = &state.conf.locker;
+    #[cfg(not(feature = "aws_kms"))]
+    let jwekey = &state.conf.jwekey;
+    #[cfg(feature = "aws_kms")]
+    let jwekey = &state.kms_secrets;
+
+    let request = generate_fingerprint_request(jwekey, locker, payload, locker_choice).await?;
+    let response = services::call_connector_api(state, request)
+        .await
+        .change_context(errors::VaultError::GenerateFingerprintFailed);
+    let jwe_body: encryption::JweBody = response
+        .get_response_inner("JweBody")
+        .change_context(errors::VaultError::GenerateFingerprintFailed)?;
+
+    let decrypted_payload =
+        decrypt_generate_fingerprint_response_payload(jwekey, jwe_body, Some(locker_choice))
+            .await
+            .change_context(errors::VaultError::GenerateFingerprintFailed)
+            .attach_printable("Error getting decrypted fingerprint response payload")?;
+    let generate_fingerprint_response: blocklist::GenerateFingerprintResponsePayload =
+        decrypted_payload
+            .parse_struct("GenerateFingerprintResponse")
+            .change_context(errors::VaultError::ResponseDeserializationFailed)?;
+
+    Ok(generate_fingerprint_response)
+}
+
+async fn decrypt_generate_fingerprint_response_payload(
+    #[cfg(not(feature = "aws_kms"))] jwekey: &settings::Jwekey,
+    #[cfg(feature = "aws_kms")] jwekey: &settings::ActiveKmsSecrets,
+    jwe_body: encryption::JweBody,
+    locker_choice: Option<api_enums::LockerChoice>,
+) -> CustomResult<String, errors::VaultError> {
+    let target_locker = locker_choice.unwrap_or(api_enums::LockerChoice::HyperswitchCardVault);
+
+    #[cfg(feature = "aws_kms")]
+    let public_key = match target_locker {
+        api_enums::LockerChoice::HyperswitchCardVault => {
+            jwekey.jwekey.peek().vault_encryption_key.as_bytes()
+        }
+    };
+
+    #[cfg(feature = "aws_kms")]
+    let private_key = jwekey.jwekey.peek().vault_private_key.as_bytes();
+
+    #[cfg(not(feature = "aws_kms"))]
+    let public_key = match target_locker {
+        api_enums::LockerChoice::HyperswitchCardVault => jwekey.vault_encryption_key.as_bytes(),
+    };
+
+    #[cfg(not(feature = "aws_kms"))]
+    let private_key = jwekey.vault_private_key.as_bytes();
+
+    let jwt = payment_methods::get_dotted_jwe(jwe_body);
+    let alg = jwe::RSA_OAEP;
+
+    let jwe_decrypted = encryption::decrypt_jwe(
+        &jwt,
+        encryption::KeyIdCheck::SkipKeyIdCheck,
+        private_key,
+        alg,
+    )
+    .await
+    .change_context(errors::VaultError::SaveCardFailed)
+    .attach_printable("Jwe Decryption failed for JweBody for vault")?;
+
+    let jws = jwe_decrypted
+        .parse_struct("JwsBody")
+        .change_context(errors::VaultError::ResponseDeserializationFailed)?;
+    let jws_body = payment_methods::get_dotted_jws(jws);
+
+    encryption::verify_sign(jws_body, public_key)
+        .change_context(errors::VaultError::SaveCardFailed)
+        .attach_printable("Jws Decryption failed for JwsBody for vault")
+}
diff --git a/crates/router/src/core/blocklist/utils.rs b/crates/router/src/core/blocklist/utils.rs
index 733b565beed..68cdb2690b7 100644
--- a/crates/router/src/core/blocklist/utils.rs
+++ b/crates/router/src/core/blocklist/utils.rs
@@ -1,15 +1,11 @@
 use api_models::blocklist as api_blocklist;
 use common_enums::MerchantDecision;
-use common_utils::{
-    crypto::{self, SignMessage},
-    errors::CustomResult,
-};
+use common_utils::errors::CustomResult;
 use diesel_models::configs;
 use error_stack::{IntoReport, ResultExt};
-#[cfg(feature = "aws_kms")]
-use external_services::aws_kms;
+use masking::StrongSecret;
 
-use super::{errors, AppState};
+use super::{errors, transformers::generate_fingerprint, AppState};
 use crate::{
     consts,
     core::{
@@ -35,52 +31,13 @@ pub async fn delete_entry_from_blocklist(
             delete_card_bin_blocklist_entry(state, &xbin, &merchant_id).await?
         }
 
-        api_blocklist::DeleteFromBlocklistRequest::Fingerprint(fingerprint_id) => {
-            let blocklist_fingerprint = state
-                .store
-                .find_blocklist_fingerprint_by_merchant_id_fingerprint_id(
-                    &merchant_id,
-                    &fingerprint_id,
-                )
-                .await
-                .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
-                    message: "blocklist record with given fingerprint id not found".to_string(),
-                })?;
-
-            #[cfg(feature = "aws_kms")]
-            let decrypted_fingerprint = aws_kms::core::get_aws_kms_client(&state.conf.kms)
-                .await
-                .decrypt(blocklist_fingerprint.encrypted_fingerprint)
-                .await
-                .change_context(errors::ApiErrorResponse::InternalServerError)
-                .attach_printable("failed to kms decrypt fingerprint")?;
-
-            #[cfg(not(feature = "aws_kms"))]
-            let decrypted_fingerprint = blocklist_fingerprint.encrypted_fingerprint;
-
-            let blocklist_entry = state
-                .store
-                .delete_blocklist_entry_by_merchant_id_fingerprint_id(&merchant_id, &fingerprint_id)
-                .await
-                .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
-                    message: "no blocklist record for the given fingerprint id was found"
-                        .to_string(),
-                })?;
-
-            state
-                .store
-                .delete_blocklist_lookup_entry_by_merchant_id_fingerprint(
-                    &merchant_id,
-                    &decrypted_fingerprint,
-                )
-                .await
-                .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
-                    message: "no blocklist record for the given fingerprint id was found"
-                        .to_string(),
-                })?;
-
-            blocklist_entry
-        }
+        api_blocklist::DeleteFromBlocklistRequest::Fingerprint(fingerprint_id) => state
+            .store
+            .delete_blocklist_entry_by_merchant_id_fingerprint_id(&merchant_id, &fingerprint_id)
+            .await
+            .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
+                message: "no blocklist record for the given fingerprint id was found".to_string(),
+            })?,
     };
 
     Ok(blocklist_entry.foreign_into())
@@ -232,57 +189,20 @@ pub async fn insert_entry_into_blocklist(
                 }
             }
 
-            let blocklist_fingerprint = state
-                .store
-                .find_blocklist_fingerprint_by_merchant_id_fingerprint_id(
-                    &merchant_id,
-                    fingerprint_id,
-                )
-                .await
-                .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
-                    message: "fingerprint not found".to_string(),
-                })?;
-
-            #[cfg(feature = "aws_kms")]
-            let decrypted_fingerprint = aws_kms::core::get_aws_kms_client(&state.conf.kms)
-                .await
-                .decrypt(blocklist_fingerprint.encrypted_fingerprint)
-                .await
-                .change_context(errors::ApiErrorResponse::InternalServerError)
-                .attach_printable("failed to kms decrypt encrypted fingerprint")?;
-
-            #[cfg(not(feature = "aws_kms"))]
-            let decrypted_fingerprint = blocklist_fingerprint.encrypted_fingerprint;
-
-            state
-                .store
-                .insert_blocklist_lookup_entry(
-                    diesel_models::blocklist_lookup::BlocklistLookupNew {
-                        merchant_id: merchant_id.clone(),
-                        fingerprint: decrypted_fingerprint,
-                    },
-                )
-                .await
-                .to_duplicate_response(errors::ApiErrorResponse::PreconditionFailed {
-                    message: "the payment instrument associated with the given fingerprint is already in the blocklist".to_string(),
-                })
-                .attach_printable("failed to add fingerprint to blocklist lookup")?;
-
             state
                 .store
                 .insert_blocklist_entry(storage::BlocklistNew {
                     merchant_id: merchant_id.clone(),
                     fingerprint_id: fingerprint_id.clone(),
-                    data_kind: blocklist_fingerprint.data_kind,
+                    data_kind: api_models::enums::enums::BlocklistDataKind::PaymentMethod,
                     metadata: None,
                     created_at: common_utils::date_time::now(),
                 })
                 .await
                 .change_context(errors::ApiErrorResponse::InternalServerError)
-                .attach_printable("failed to add fingerprint to pm blocklist")?
+                .attach_printable("failed to add fingerprint to blocklist")?
         }
     };
-
     Ok(blocklist_entry.foreign_into())
 }
 
@@ -330,17 +250,6 @@ async fn duplicate_check_insert_bin(
     merchant_id: &str,
     data_kind: common_enums::BlocklistDataKind,
 ) -> RouterResult<storage::Blocklist> {
-    let merchant_secret = get_merchant_fingerprint_secret(state, merchant_id).await?;
-    let bin_fingerprint = crypto::HmacSha512::sign_message(
-        &crypto::HmacSha512,
-        merchant_secret.clone().as_bytes(),
-        bin.as_bytes(),
-    )
-    .change_context(errors::ApiErrorResponse::InternalServerError)
-    .attach_printable("error in bin hash creation")?;
-
-    let encoded_fingerprint = hex::encode(bin_fingerprint.clone());
-
     let blocklist_entry_result = state
         .store
         .find_blocklist_entry_by_merchant_id_fingerprint_id(merchant_id, bin)
@@ -363,17 +272,6 @@ async fn duplicate_check_insert_bin(
         }
     }
 
-    // Checking for duplicacy
-    state
-        .store
-        .insert_blocklist_lookup_entry(diesel_models::blocklist_lookup::BlocklistLookupNew {
-            merchant_id: merchant_id.to_string(),
-            fingerprint: encoded_fingerprint.clone(),
-        })
-        .await
-        .change_context(errors::ApiErrorResponse::InternalServerError)
-        .attach_printable("error inserting blocklist lookup entry")?;
-
     state
         .store
         .insert_blocklist_entry(storage::BlocklistNew {
@@ -393,21 +291,6 @@ async fn delete_card_bin_blocklist_entry(
     bin: &str,
     merchant_id: &str,
 ) -> RouterResult<storage::Blocklist> {
-    let merchant_secret = get_merchant_fingerprint_secret(state, merchant_id).await?;
-    let bin_fingerprint = crypto::HmacSha512
-        .sign_message(merchant_secret.as_bytes(), bin.as_bytes())
-        .change_context(errors::ApiErrorResponse::InternalServerError)
-        .attach_printable("error when hashing card bin")?;
-    let encoded_fingerprint = hex::encode(bin_fingerprint);
-
-    state
-        .store
-        .delete_blocklist_lookup_entry_by_merchant_id_fingerprint(merchant_id, &encoded_fingerprint)
-        .await
-        .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
-            message: "could not find a blocklist entry for the given bin".to_string(),
-        })?;
-
     state
         .store
         .delete_blocklist_entry_by_merchant_id_fingerprint_id(merchant_id, bin)
@@ -431,28 +314,28 @@ where
         get_merchant_fingerprint_secret(state, merchant_id.as_str()).await?;
 
     // Hashed Fingerprint to check whether or not this payment should be blocked.
-    let card_number_fingerprint = payment_data
-        .payment_method_data
-        .as_ref()
-        .and_then(|pm_data| match pm_data {
-            api_models::payments::PaymentMethodData::Card(card) => {
-                crypto::HmacSha512::sign_message(
-                    &crypto::HmacSha512,
-                    merchant_fingerprint_secret.as_bytes(),
-                    card.card_number.clone().get_card_no().as_bytes(),
-                )
-                .attach_printable("error in pm fingerprint creation")
-                .map_or_else(
-                    |err| {
-                        logger::error!(error=?err);
-                        None
-                    },
-                    Some,
-                )
-            }
-            _ => None,
-        })
-        .map(hex::encode);
+    let card_number_fingerprint = if let Some(api_models::payments::PaymentMethodData::Card(card)) =
+        payment_data.payment_method_data.as_ref()
+    {
+        generate_fingerprint(
+            state,
+            StrongSecret::new(card.card_number.clone().get_card_no()),
+            StrongSecret::new(merchant_fingerprint_secret.clone()),
+            api_models::enums::LockerChoice::HyperswitchCardVault,
+        )
+        .await
+        .attach_printable("error in pm fingerprint creation")
+        .map_or_else(
+            |err| {
+                logger::error!(error=?err);
+                None
+            },
+            Some,
+        )
+        .map(|payload| payload.card_fingerprint)
+    } else {
+        None
+    };
 
     // Hashed Cardbin to check whether or not this payment should be blocked.
     let card_bin_fingerprint = payment_data
@@ -460,66 +343,43 @@ where
         .as_ref()
         .and_then(|pm_data| match pm_data {
             api_models::payments::PaymentMethodData::Card(card) => {
-                crypto::HmacSha512::sign_message(
-                    &crypto::HmacSha512,
-                    merchant_fingerprint_secret.as_bytes(),
-                    card.card_number.clone().get_card_isin().as_bytes(),
-                )
-                .attach_printable("error in card bin hash creation")
-                .map_or_else(
-                    |err| {
-                        logger::error!(error=?err);
-                        None
-                    },
-                    Some,
-                )
+                Some(card.card_number.clone().get_card_isin())
             }
             _ => None,
-        })
-        .map(hex::encode);
+        });
 
     // Hashed Extended Cardbin to check whether or not this payment should be blocked.
-    let extended_card_bin_fingerprint = payment_data
-        .payment_method_data
-        .as_ref()
-        .and_then(|pm_data| match pm_data {
-            api_models::payments::PaymentMethodData::Card(card) => {
-                crypto::HmacSha512::sign_message(
-                    &crypto::HmacSha512,
-                    merchant_fingerprint_secret.as_bytes(),
-                    card.card_number.clone().get_extended_card_bin().as_bytes(),
-                )
-                .attach_printable("error in extended card bin hash creation")
-                .map_or_else(
-                    |err| {
-                        logger::error!(error=?err);
-                        None
-                    },
-                    Some,
-                )
-            }
-            _ => None,
-        })
-        .map(hex::encode);
+    let extended_card_bin_fingerprint =
+        payment_data
+            .payment_method_data
+            .as_ref()
+            .and_then(|pm_data| match pm_data {
+                api_models::payments::PaymentMethodData::Card(card) => {
+                    Some(card.card_number.clone().get_extended_card_bin())
+                }
+                _ => None,
+            });
 
     //validating the payment method.
     let mut blocklist_futures = Vec::new();
     if let Some(card_number_fingerprint) = card_number_fingerprint.as_ref() {
-        blocklist_futures.push(db.find_blocklist_lookup_entry_by_merchant_id_fingerprint(
+        blocklist_futures.push(db.find_blocklist_entry_by_merchant_id_fingerprint_id(
             merchant_id,
             card_number_fingerprint,
         ));
     }
 
     if let Some(card_bin_fingerprint) = card_bin_fingerprint.as_ref() {
-        blocklist_futures.push(db.find_blocklist_lookup_entry_by_merchant_id_fingerprint(
-            merchant_id,
-            card_bin_fingerprint,
-        ));
+        blocklist_futures.push(
+            db.find_blocklist_entry_by_merchant_id_fingerprint_id(
+                merchant_id,
+                card_bin_fingerprint,
+            ),
+        );
     }
 
     if let Some(extended_card_bin_fingerprint) = extended_card_bin_fingerprint.as_ref() {
-        blocklist_futures.push(db.find_blocklist_lookup_entry_by_merchant_id_fingerprint(
+        blocklist_futures.push(db.find_blocklist_entry_by_merchant_id_fingerprint_id(
             merchant_id,
             extended_card_bin_fingerprint,
         ));
@@ -538,7 +398,6 @@ where
             }
         }
     }
-
     if should_payment_be_blocked {
         // Update db for attempt and intent status.
         db.update_payment_intent(
@@ -582,13 +441,12 @@ where
         }
         .into())
     } else {
-        payment_data.payment_intent.fingerprint_id = generate_payment_fingerprint(
+        payment_data.payment_attempt.fingerprint_id = generate_payment_fingerprint(
             state,
             payment_data.payment_attempt.merchant_id.clone(),
             payment_data.payment_method_data.clone(),
         )
         .await?;
-
         Ok(false)
     }
 }
@@ -598,68 +456,31 @@ pub async fn generate_payment_fingerprint(
     merchant_id: String,
     payment_method_data: Option<crate::types::api::PaymentMethodData>,
 ) -> CustomResult<Option<String>, errors::ApiErrorResponse> {
-    let db = &state.store;
     let merchant_fingerprint_secret = get_merchant_fingerprint_secret(state, &merchant_id).await?;
-    let card_number_fingerprint = payment_method_data
-        .as_ref()
-        .and_then(|pm_data| match pm_data {
-            api_models::payments::PaymentMethodData::Card(card) => {
-                crypto::HmacSha512::sign_message(
-                    &crypto::HmacSha512,
-                    merchant_fingerprint_secret.as_bytes(),
-                    card.card_number.clone().get_card_no().as_bytes(),
-                )
-                .attach_printable("error in pm fingerprint creation")
-                .map_or_else(
-                    |err| {
-                        logger::error!(error=?err);
-                        None
-                    },
-                    Some,
-                )
-            }
-            _ => None,
-        })
-        .map(hex::encode);
 
-    let mut fingerprint_id = None;
-    if let Some(encoded_hash) = card_number_fingerprint {
-        #[cfg(feature = "kms")]
-        let encrypted_fingerprint = kms::get_kms_client(&state.conf.kms)
-            .await
-            .encrypt(encoded_hash)
+    Ok(
+        if let Some(api_models::payments::PaymentMethodData::Card(card)) =
+            payment_method_data.as_ref()
+        {
+            generate_fingerprint(
+                state,
+                StrongSecret::new(card.card_number.clone().get_card_no()),
+                StrongSecret::new(merchant_fingerprint_secret),
+                api_models::enums::LockerChoice::HyperswitchCardVault,
+            )
             .await
+            .attach_printable("error in pm fingerprint creation")
             .map_or_else(
-                |e| {
-                    logger::error!(error=?e, "failed kms encryption of card fingerprint");
+                |err| {
+                    logger::error!(error=?err);
                     None
                 },
                 Some,
-            );
-
-        #[cfg(not(feature = "kms"))]
-        let encrypted_fingerprint = Some(encoded_hash);
-
-        if let Some(encrypted_fingerprint) = encrypted_fingerprint {
-            fingerprint_id = db
-                .insert_blocklist_fingerprint_entry(
-                    diesel_models::blocklist_fingerprint::BlocklistFingerprintNew {
-                        merchant_id,
-                        fingerprint_id: utils::generate_id(consts::ID_LENGTH, "fingerprint"),
-                        encrypted_fingerprint,
-                        data_kind: common_enums::BlocklistDataKind::PaymentMethod,
-                        created_at: common_utils::date_time::now(),
-                    },
-                )
-                .await
-                .map_or_else(
-                    |e| {
-                        logger::error!(error=?e, "failed storing card fingerprint in db");
-                        None
-                    },
-                    |fp| Some(fp.fingerprint_id),
-                );
-        }
-    }
-    Ok(fingerprint_id)
+            )
+            .map(|payload| payload.card_fingerprint)
+        } else {
+            logger::error!("failed to retrieve card fingerprint");
+            None
+        },
+    )
 }
diff --git a/crates/router/src/core/errors.rs b/crates/router/src/core/errors.rs
index d1814f7ee82..cec563b9631 100644
--- a/crates/router/src/core/errors.rs
+++ b/crates/router/src/core/errors.rs
@@ -236,6 +236,8 @@ pub enum VaultError {
     FetchPaymentMethodFailed,
     #[error("Failed to save payment method in vault")]
     SavePaymentMethodFailed,
+    #[error("Failed to generate fingerprint")]
+    GenerateFingerprintFailed,
 }
 
 #[derive(Debug, thiserror::Error)]
diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs
index 635ba917168..764752ba903 100644
--- a/crates/router/src/core/payments/helpers.rs
+++ b/crates/router/src/core/payments/helpers.rs
@@ -3202,6 +3202,7 @@ impl AttemptType {
             unified_message: None,
             net_amount: old_payment_attempt.amount,
             mandate_data: old_payment_attempt.mandate_data,
+            fingerprint_id: None,
         }
     }
 
diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs
index 734839c9b6d..2d2ee0e8728 100644
--- a/crates/router/src/core/payments/operations/payment_confirm.rs
+++ b/crates/router/src/core/payments/operations/payment_confirm.rs
@@ -739,6 +739,7 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
         let m_straight_through_algorithm = straight_through_algorithm.clone();
         let m_error_code = error_code.clone();
         let m_error_message = error_message.clone();
+        let m_fingerprint_id = payment_data.payment_attempt.fingerprint_id.clone();
         let m_db = state.clone().store;
         let surcharge_amount = payment_data
             .surcharge_details
@@ -774,6 +775,7 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
                         merchant_connector_id,
                         surcharge_amount,
                         tax_amount,
+                        fingerprint_id: m_fingerprint_id,
                     },
                     storage_scheme,
                 )
@@ -784,7 +786,6 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
         );
 
         let m_payment_data_payment_intent = payment_data.payment_intent.clone();
-        let m_fingerprint_id = payment_data.payment_intent.fingerprint_id.clone();
         let m_customer_id = customer_id.clone();
         let m_shipping_address_id = shipping_address.clone();
         let m_billing_address_id = billing_address.clone();
@@ -821,7 +822,7 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
                         metadata: m_metadata,
                         payment_confirm_source: header_payload.payment_confirm_source,
                         updated_by: m_storage_scheme,
-                        fingerprint_id: m_fingerprint_id,
+                        fingerprint_id: None,
                         session_expiry,
                     },
                     storage_scheme,
diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs
index 6bd6ca91100..099eae71800 100644
--- a/crates/router/src/core/payments/operations/payment_response.rs
+++ b/crates/router/src/core/payments/operations/payment_response.rs
@@ -604,6 +604,8 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
                     };
 
                     if router_data.status == enums::AttemptStatus::Charged {
+                        payment_data.payment_intent.fingerprint_id =
+                            payment_data.payment_attempt.fingerprint_id.clone();
                         metrics::SUCCESSFUL_PAYMENT.add(&metrics::CONTEXT, 1, &[]);
                     }
 
@@ -798,6 +800,7 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
             return_url: router_data.return_url.clone(),
             amount_captured,
             updated_by: storage_scheme.to_string(),
+            fingerprint_id: payment_data.payment_attempt.fingerprint_id.clone(),
             incremental_authorization_allowed: payment_data
                 .payment_intent
                 .incremental_authorization_allowed,
diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs
index 0ec11e39f62..071d919eb19 100644
--- a/crates/router/src/core/payments/operations/payment_update.rs
+++ b/crates/router/src/core/payments/operations/payment_update.rs
@@ -559,6 +559,7 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
                     capture_method,
                     surcharge_amount,
                     tax_amount,
+                    fingerprint_id: None,
                     updated_by: storage_scheme.to_string(),
                 },
                 storage_scheme,
diff --git a/crates/router/src/services/api/client.rs b/crates/router/src/services/api/client.rs
index f4d74c4f81b..a2cf65ce66b 100644
--- a/crates/router/src/services/api/client.rs
+++ b/crates/router/src/services/api/client.rs
@@ -114,6 +114,7 @@ pub fn proxy_bypass_urls(locker: &Locker) -> Vec<String> {
     let locker_host_rs = locker.host_rs.to_owned();
     vec![
         format!("{locker_host}/cards/add"),
+        format!("{locker_host}/cards/fingerprint"),
         format!("{locker_host}/cards/retrieve"),
         format!("{locker_host}/cards/delete"),
         format!("{locker_host_rs}/cards/add"),
diff --git a/crates/storage_impl/src/mock_db/payment_attempt.rs b/crates/storage_impl/src/mock_db/payment_attempt.rs
index e60e32227d3..5add5036f08 100644
--- a/crates/storage_impl/src/mock_db/payment_attempt.rs
+++ b/crates/storage_impl/src/mock_db/payment_attempt.rs
@@ -148,6 +148,7 @@ impl PaymentAttemptInterface for MockDb {
             unified_code: payment_attempt.unified_code,
             unified_message: payment_attempt.unified_message,
             mandate_data: payment_attempt.mandate_data,
+            fingerprint_id: payment_attempt.fingerprint_id,
         };
         payment_attempts.push(payment_attempt.clone());
         Ok(payment_attempt)
diff --git a/crates/storage_impl/src/payments/payment_attempt.rs b/crates/storage_impl/src/payments/payment_attempt.rs
index ec19e30c0ec..a51f95e1e74 100644
--- a/crates/storage_impl/src/payments/payment_attempt.rs
+++ b/crates/storage_impl/src/payments/payment_attempt.rs
@@ -391,6 +391,7 @@ impl<T: DatabaseStore> PaymentAttemptInterface for KVRouterStore<T> {
                     unified_code: payment_attempt.unified_code.clone(),
                     unified_message: payment_attempt.unified_message.clone(),
                     mandate_data: payment_attempt.mandate_data.clone(),
+                    fingerprint_id: payment_attempt.fingerprint_id.clone(),
                 };
 
                 let field = format!("pa_{}", created_attempt.attempt_id);
@@ -1105,6 +1106,7 @@ impl DataModelExt for PaymentAttempt {
             unified_code: self.unified_code,
             unified_message: self.unified_message,
             mandate_data: self.mandate_data.map(|d| d.to_storage_model()),
+            fingerprint_id: self.fingerprint_id,
         }
     }
 
@@ -1163,6 +1165,7 @@ impl DataModelExt for PaymentAttempt {
             mandate_data: storage_model
                 .mandate_data
                 .map(MandateDetails::from_storage_model),
+            fingerprint_id: storage_model.fingerprint_id,
         }
     }
 }
@@ -1219,6 +1222,7 @@ impl DataModelExt for PaymentAttemptNew {
             unified_code: self.unified_code,
             unified_message: self.unified_message,
             mandate_data: self.mandate_data.map(|d| d.to_storage_model()),
+            fingerprint_id: self.fingerprint_id,
         }
     }
 
@@ -1275,6 +1279,7 @@ impl DataModelExt for PaymentAttemptNew {
             mandate_data: storage_model
                 .mandate_data
                 .map(MandateDetails::from_storage_model),
+            fingerprint_id: storage_model.fingerprint_id,
         }
     }
 }
@@ -1299,6 +1304,7 @@ impl DataModelExt for PaymentAttemptUpdate {
                 capture_method,
                 surcharge_amount,
                 tax_amount,
+                fingerprint_id,
                 updated_by,
             } => DieselPaymentAttemptUpdate::Update {
                 amount,
@@ -1315,6 +1321,7 @@ impl DataModelExt for PaymentAttemptUpdate {
                 capture_method,
                 surcharge_amount,
                 tax_amount,
+                fingerprint_id,
                 updated_by,
             },
             Self::UpdateTrackers {
@@ -1373,6 +1380,7 @@ impl DataModelExt for PaymentAttemptUpdate {
                 amount_capturable,
                 surcharge_amount,
                 tax_amount,
+                fingerprint_id,
                 updated_by,
                 merchant_connector_id: connector_id,
             } => DieselPaymentAttemptUpdate::ConfirmUpdate {
@@ -1394,6 +1402,7 @@ impl DataModelExt for PaymentAttemptUpdate {
                 amount_capturable,
                 surcharge_amount,
                 tax_amount,
+                fingerprint_id,
                 updated_by,
                 merchant_connector_id: connector_id,
             },
@@ -1578,6 +1587,7 @@ impl DataModelExt for PaymentAttemptUpdate {
                 capture_method,
                 surcharge_amount,
                 tax_amount,
+                fingerprint_id,
                 updated_by,
             } => Self::Update {
                 amount,
@@ -1594,6 +1604,7 @@ impl DataModelExt for PaymentAttemptUpdate {
                 capture_method,
                 surcharge_amount,
                 tax_amount,
+                fingerprint_id,
                 updated_by,
             },
             DieselPaymentAttemptUpdate::UpdateTrackers {
@@ -1641,6 +1652,7 @@ impl DataModelExt for PaymentAttemptUpdate {
                 amount_capturable,
                 surcharge_amount,
                 tax_amount,
+                fingerprint_id,
                 updated_by,
                 merchant_connector_id: connector_id,
             } => Self::ConfirmUpdate {
@@ -1662,6 +1674,7 @@ impl DataModelExt for PaymentAttemptUpdate {
                 amount_capturable,
                 surcharge_amount,
                 tax_amount,
+                fingerprint_id,
                 updated_by,
                 merchant_connector_id: connector_id,
             },
diff --git a/crates/storage_impl/src/payments/payment_intent.rs b/crates/storage_impl/src/payments/payment_intent.rs
index 7efbafb662d..d201d2a053a 100644
--- a/crates/storage_impl/src/payments/payment_intent.rs
+++ b/crates/storage_impl/src/payments/payment_intent.rs
@@ -925,12 +925,14 @@ impl DataModelExt for PaymentIntentUpdate {
             Self::ResponseUpdate {
                 status,
                 amount_captured,
+                fingerprint_id,
                 return_url,
                 updated_by,
                 incremental_authorization_allowed,
             } => DieselPaymentIntentUpdate::ResponseUpdate {
                 status,
                 amount_captured,
+                fingerprint_id,
                 return_url,
                 updated_by,
                 incremental_authorization_allowed,
diff --git a/migrations/2024-02-12-135546_add_fingerprint_id_in_payment_attempt/down.sql b/migrations/2024-02-12-135546_add_fingerprint_id_in_payment_attempt/down.sql
new file mode 100644
index 00000000000..c6647bd3ab0
--- /dev/null
+++ b/migrations/2024-02-12-135546_add_fingerprint_id_in_payment_attempt/down.sql
@@ -0,0 +1,2 @@
+-- This file should undo anything in `up.sql`
+ALTER TABLE payment_attempt DROP COLUMN IF EXISTS fingerprint_id;
diff --git a/migrations/2024-02-12-135546_add_fingerprint_id_in_payment_attempt/up.sql b/migrations/2024-02-12-135546_add_fingerprint_id_in_payment_attempt/up.sql
new file mode 100644
index 00000000000..8d219935234
--- /dev/null
+++ b/migrations/2024-02-12-135546_add_fingerprint_id_in_payment_attempt/up.sql
@@ -0,0 +1,2 @@
+-- Your SQL goes here
+ALTER TABLE payment_attempt ADD COLUMN IF NOT EXISTS fingerprint_id VARCHAR(64);

## Type of Change
<!-- Put an `x` in the boxes that apply -->
Refactoring the present blocklist flow and storing the fingerprint on our locker end for more security and efficiency.
More details are here in this [issue](https://github.com/juspay/hyperswitch/issues/3344).
- [ ] Bugfix
- [ ] New feature
- [ ] Enhancement
- [x] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
<!-- Describe your changes in detail -->
After this change we will store the fingerprints on our locker in fingerprints table. No more storing anything related to fingerprints in our DB. There will be only one table usable left for blocklsit which will be `blocklist` (stores all the fingerprints blocked by a merchant with their type and metadata).
Previously we were generating the fingerprints on our application end itself after this change we will have the fingerprints generated by our locker.
Fingerprint is generated for each and every card payment. Moreover we are storing fingerprint in all the attempt tables.
Now vouching for the scenarios were there are more than one attempt associated with one intent in that case we are adding the successful attempt's fingerprint in intent.

## Required DB changes
purpose: support for addition of fingerprint_id in payment_attempt table
Query:
`ALTER TABLE payment_attempt ADD COLUMN IF NOT EXISTS fingerprint_id VARCHAR(64);`

So how does the whole flow works:
1. Before confirmation of payment the payment instrument's fingerprint is cross-checked with the blocklist.
2. If it is present in blocklist the payment will be blocked (status as false).
3. This payment can be retried with another instrument and in case if succeeds the fingerprint will be added in intent.

**Note**: All successful attempts will have a fingerprint but intent's fingerprint will always be the successful attempt's fingerprint.

### Other checkpoints under same hood
- [x] Instead of generating the fingerprint in application add support for `/cards/fingerprint` API call with `hash_key`.
- [x] whitelisting mentioned endpoint in proxy.
- [x] Storing the fingerprint in payment attempt and payment intent table.
- [x] Refactoring Block List checking using the fingerprint API.


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

### Generating fingerprints
-> Toggle the blocklist guard from merchant account being used using `/blocklist/toggle?status=true`. More about 
     toggling guard [here](https://github.com/juspay/hyperswitch/issues/3467).
-> We need to create a payment.
-> While trying to confirm the payment it will have the `fingerprint_id` in the response. This can be used to block the 
     instrument. If the payment was able to be captured the fingerprint will be stored in the intent table as well

### Blocking fingerprints
Refer to the attached postman collection for the API contracts for the blocklist APIs([Description](https://github.com/juspay/hyperswitch/issues/3439)). Currently we support blocking three types of resources i.e. card numbers (payment intrument), card bin, and extended card bin.
[blocklist_api_postman.zip](https://github.com/juspay/hyperswitch/files/13893696/blocklist_api_postman.zip)

```
For Card Bin and Extended Card Bin :-
1. Setup a Merchant Account and any Connector account
2. Make a payment with a certain card (ensure it succeeds)
3. Block the card's card bin or extended card bin
4. Try the payment again (should fail this time with an API response saying that the payment was blocked)

For Payment Instrument :-
1. Repeat steps 1 and 2 of previous section
2. In the payment confirm response, there will be an additional field called "fingerprint". This is the fingerprint id that can be used to block a particular payment method. Use this to block the card.
3. Try the payment again (should fail)
```
The respected curls are mentioned in the issue.

## 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
- [ ] I added a [CHANGELOG](/CHANGELOG.md) entry if applicable
