            merchant_key_store.key.get_inner(),
        )
        .await?;
        add_delete_tokenized_data_task(&*state.store, &lookup_key, pm).await?;
        metrics::TOKENIZED_DATA_COUNT.add(1, &[]);
        Ok(lookup_key)
    }

    #[cfg(feature = "payouts")]
    #[instrument(skip_all)]
    pub async fn get_payout_method_data_from_temporary_locker(
        state: &routes::SessionState,
        lookup_key: &str,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> RouterResult<(Option<api::PayoutMethodData>, SupplementaryVaultData)> {
        let de_tokenize =
            get_tokenized_data(state, lookup_key, true, merchant_key_store.key.get_inner()).await?;
        let (payout_method, supp_data) =
            api::PayoutMethodData::from_values(de_tokenize.value1, de_tokenize.value2)
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Error parsing Payout Method from Values")?;

        Ok((Some(payout_method), supp_data))
    }

    #[cfg(feature = "payouts")]
    #[instrument(skip_all)]
    pub async fn store_payout_method_data_in_locker(
        state: &routes::SessionState,
        token_id: Option<String>,
        payout_method: &api::PayoutMethodData,
        customer_id: Option<id_type::CustomerId>,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> RouterResult<String> {
        let value1 = payout_method
            .get_value1(customer_id.clone())
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Error getting Value1 for locker")?;

        let value2 = payout_method
            .get_value2(customer_id)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Error getting Value2 for locker")?;

        let lookup_key =
            token_id.unwrap_or_else(|| generate_id_with_default_len("temporary_token"));

        let lookup_key = create_tokenize(
            state,
            value1,
            Some(value2),
            lookup_key,
            merchant_key_store.key.get_inner(),
        )
        .await?;
        // add_delete_tokenized_data_task(&*state.store, &lookup_key, pm).await?;
        // scheduler_metrics::TOKENIZED_DATA_COUNT.add(1, &[]);
        Ok(lookup_key)
    }

    #[instrument(skip_all)]
    pub async fn delete_locker_payment_method_by_lookup_key(
        state: &routes::SessionState,
        lookup_key: &Option<String>,
    ) {
        if let Some(lookup_key) = lookup_key {
            delete_tokenized_data(state, lookup_key)
                .await
                .map(|_| logger::info!("Card From locker deleted Successfully"))
                .map_err(|err| logger::error!("Error: Deleting Card From Redis Locker : {:?}", err))
                .ok();
        }
    }
}

//------------------------------------------------TokenizeService------------------------------------------------

#[inline(always)]
fn get_redis_locker_key(lookup_key: &str) -> String {
    format!("{}_{}", consts::LOCKER_REDIS_PREFIX, lookup_key)
}

#[instrument(skip(state, value1, value2))]
pub async fn create_tokenize(
    state: &routes::SessionState,
    value1: String,
    value2: Option<String>,
    lookup_key: String,
    encryption_key: &masking::Secret<Vec<u8>>,
) -> RouterResult<String> {
    let redis_key = get_redis_locker_key(lookup_key.as_str());
    let func = || async {
        metrics::CREATED_TOKENIZED_CARD.add(1, &[]);

        let payload_to_be_encrypted = api::TokenizePayloadRequest {
            value1: value1.clone(),
            value2: value2.clone().unwrap_or_default(),
            lookup_key: lookup_key.clone(),
            service_name: VAULT_SERVICE_NAME.to_string(),
        };

        let payload = payload_to_be_encrypted
            .encode_to_string_of_json()
            .change_context(errors::ApiErrorResponse::InternalServerError)?;

        let encrypted_payload = GcmAes256
            .encode_message(encryption_key.peek().as_ref(), payload.as_bytes())
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to encode redis temp locker data")?;

        let redis_conn = state
            .store
            .get_redis_conn()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to get redis connection")?;

        redis_conn
            .set_key_if_not_exists_with_expiry(
                &redis_key.as_str().into(),
                bytes::Bytes::from(encrypted_payload),
                Some(i64::from(consts::LOCKER_REDIS_EXPIRY_SECONDS)),
            )
            .await
            .map(|_| lookup_key.clone())
            .inspect_err(|error| {
                metrics::TEMP_LOCKER_FAILURES.add(1, &[]);
                logger::error!(?error, "Failed to store tokenized data in Redis");
            })
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Error from redis locker")
    };

    match func().await {
        Ok(s) => {
            logger::info!(
                "Insert payload in redis locker successful with lookup key: {:?}",
                redis_key
            );
            Ok(s)
        }
        Err(err) => {
            logger::error!("Redis Temp locker Failed: {:?}", err);
            Err(err)
        }
    }
}

#[instrument(skip(state))]
pub async fn get_tokenized_data(
    state: &routes::SessionState,
    lookup_key: &str,
    _should_get_value2: bool,
    encryption_key: &masking::Secret<Vec<u8>>,
) -> RouterResult<api::TokenizePayloadRequest> {
    let redis_key = get_redis_locker_key(lookup_key);
    let func = || async {
        metrics::GET_TOKENIZED_CARD.add(1, &[]);

        let redis_conn = state
            .store
            .get_redis_conn()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to get redis connection")?;

        let response = redis_conn
            .get_key::<bytes::Bytes>(&redis_key.as_str().into())
            .await;

        match response {
            Ok(resp) => {
                let decrypted_payload = GcmAes256
                    .decode_message(
                        encryption_key.peek().as_ref(),
                        masking::Secret::new(resp.into()),
                    )
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to decode redis temp locker data")?;

                let get_response: api::TokenizePayloadRequest =
                    bytes::Bytes::from(decrypted_payload)
                        .parse_struct("TokenizePayloadRequest")
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable(
                            "Error getting TokenizePayloadRequest from tokenize response",
                        )?;

                Ok(get_response)
            }
            Err(err) => {
                metrics::TEMP_LOCKER_FAILURES.add(1, &[]);
                Err(err).change_context(errors::ApiErrorResponse::UnprocessableEntity {
                    message: "Token is invalid or expired".into(),
                })
            }
        }
    };

    match func().await {
        Ok(s) => {
            logger::info!(
                "Fetch payload in redis locker successful with lookup key: {:?}",
                redis_key
            );
            Ok(s)
        }
        Err(err) => {
            logger::error!("Redis Temp locker Failed: {:?}", err);
            Err(err)
        }
    }
}

#[instrument(skip(state))]
pub async fn delete_tokenized_data(
    state: &routes::SessionState,
    lookup_key: &str,
) -> RouterResult<()> {
    let redis_key = get_redis_locker_key(lookup_key);
    let func = || async {
        metrics::DELETED_TOKENIZED_CARD.add(1, &[]);

        let redis_conn = state
            .store
            .get_redis_conn()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to get redis connection")?;

        let response = redis_conn.delete_key(&redis_key.as_str().into()).await;

        match response {
            Ok(redis_interface::DelReply::KeyDeleted) => Ok(()),
            Ok(redis_interface::DelReply::KeyNotDeleted) => {
                Err(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Token invalid or expired")
            }
            Err(err) => {
                metrics::TEMP_LOCKER_FAILURES.add(1, &[]);
                Err(errors::ApiErrorResponse::InternalServerError).attach_printable_lazy(|| {
                    format!("Failed to delete from redis locker: {err:?}")
                })
            }
        }
    };
    match func().await {
        Ok(s) => {
            logger::info!(
                "Delete payload in redis locker successful with lookup key: {:?}",
                redis_key
            );
            Ok(s)
        }
        Err(err) => {
            logger::error!("Redis Temp locker Failed: {:?}", err);
            Err(err)
        }
    }
}

#[cfg(feature = "v2")]
async fn create_vault_request<R: pm_types::VaultingInterface>(
    jwekey: &settings::Jwekey,
    locker: &settings::Locker,
    payload: Vec<u8>,
    tenant_id: id_type::TenantId,
) -> CustomResult<request::Request, errors::VaultError> {
    let private_key = jwekey.vault_private_key.peek().as_bytes();

    let jws = services::encryption::jws_sign_payload(
        &payload,
        &locker.locker_signing_key_id,
        private_key,
    )
    .await
    .change_context(errors::VaultError::RequestEncryptionFailed)?;

    let jwe_payload = pm_transforms::create_jwe_body_for_vault(jwekey, &jws).await?;

    let mut url = locker.host.to_owned();
    url.push_str(R::get_vaulting_request_url());
    let mut request = request::Request::new(services::Method::Post, &url);
    request.add_header(
        headers::CONTENT_TYPE,
        consts::VAULT_HEADER_CONTENT_TYPE.into(),
    );
    request.add_header(
        headers::X_TENANT_ID,
        tenant_id.get_string_repr().to_owned().into(),
    );
    request.set_body(request::RequestContent::Json(Box::new(jwe_payload)));
    Ok(request)
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn call_to_vault<V: pm_types::VaultingInterface>(
    state: &routes::SessionState,
    payload: Vec<u8>,
) -> CustomResult<String, errors::VaultError> {
    let locker = &state.conf.locker;
    let jwekey = state.conf.jwekey.get_inner();

    let request =
        create_vault_request::<V>(jwekey, locker, payload, state.tenant.tenant_id.to_owned())
            .await?;
    let response = services::call_connector_api(state, request, V::get_vaulting_flow_name())
        .await
        .change_context(errors::VaultError::VaultAPIError);

    let jwe_body: services::JweBody = response
        .get_response_inner("JweBody")
        .change_context(errors::VaultError::ResponseDeserializationFailed)
        .attach_printable("Failed to get JweBody from vault response")?;

    let decrypted_payload = pm_transforms::get_decrypted_vault_response_payload(
        jwekey,
        jwe_body,
        locker.decryption_scheme.clone(),
    )
    .await
    .change_context(errors::VaultError::ResponseDecryptionFailed)
    .attach_printable("Error getting decrypted vault response payload")?;

    Ok(decrypted_payload)
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn get_fingerprint_id_from_vault<D: domain::VaultingDataInterface + serde::Serialize>(
    state: &routes::SessionState,
    data: &D,
    key: String,
) -> CustomResult<String, errors::VaultError> {
    let data = serde_json::to_string(data)
        .change_context(errors::VaultError::RequestEncodingFailed)
        .attach_printable("Failed to encode Vaulting data to string")?;

    let payload = pm_types::VaultFingerprintRequest { key, data }
        .encode_to_vec()
        .change_context(errors::VaultError::RequestEncodingFailed)
        .attach_printable("Failed to encode VaultFingerprintRequest")?;

    let resp = call_to_vault::<pm_types::GetVaultFingerprint>(state, payload)
        .await
        .change_context(errors::VaultError::VaultAPIError)
        .attach_printable("Call to vault failed")?;

    let fingerprint_resp: pm_types::VaultFingerprintResponse = resp
        .parse_struct("VaultFingerprintResponse")
        .change_context(errors::VaultError::ResponseDeserializationFailed)
        .attach_printable("Failed to parse data into VaultFingerprintResponse")?;

    Ok(fingerprint_resp.fingerprint_id)
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn add_payment_method_to_vault(
    state: &routes::SessionState,
    merchant_context: &domain::MerchantContext,
    pmd: &domain::PaymentMethodVaultingData,
    existing_vault_id: Option<domain::VaultId>,
    customer_id: &id_type::GlobalCustomerId,
) -> CustomResult<pm_types::AddVaultResponse, errors::VaultError> {
    let payload = pm_types::AddVaultRequest {
        entity_id: customer_id.to_owned(),
        vault_id: existing_vault_id
            .unwrap_or(domain::VaultId::generate(uuid::Uuid::now_v7().to_string())),
        data: pmd,
        ttl: state.conf.locker.ttl_for_storage_in_secs,
    }
    .encode_to_vec()
    .change_context(errors::VaultError::RequestEncodingFailed)
    .attach_printable("Failed to encode AddVaultRequest")?;

    let resp = call_to_vault::<pm_types::AddVault>(state, payload)
        .await
        .change_context(errors::VaultError::VaultAPIError)
        .attach_printable("Call to vault failed")?;

    let stored_pm_resp: pm_types::AddVaultResponse = resp
        .parse_struct("AddVaultResponse")
        .change_context(errors::VaultError::ResponseDeserializationFailed)
        .attach_printable("Failed to parse data into AddVaultResponse")?;

    Ok(stored_pm_resp)
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn retrieve_payment_method_from_vault_internal(
    state: &routes::SessionState,
    merchant_context: &domain::MerchantContext,
    vault_id: &domain::VaultId,
    customer_id: &id_type::GlobalCustomerId,
) -> CustomResult<pm_types::VaultRetrieveResponse, errors::VaultError> {
    let payload = pm_types::VaultRetrieveRequest {
        entity_id: customer_id.to_owned(),
        vault_id: vault_id.to_owned(),
    }
    .encode_to_vec()
    .change_context(errors::VaultError::RequestEncodingFailed)
    .attach_printable("Failed to encode VaultRetrieveRequest")?;

    let resp = call_to_vault::<pm_types::VaultRetrieve>(state, payload)
        .await
        .change_context(errors::VaultError::VaultAPIError)
        .attach_printable("Call to vault failed")?;

    let stored_pm_resp: pm_types::VaultRetrieveResponse = resp
        .parse_struct("VaultRetrieveResponse")
        .change_context(errors::VaultError::ResponseDeserializationFailed)
        .attach_printable("Failed to parse data into VaultRetrieveResponse")?;

    Ok(stored_pm_resp)
}

#[cfg(all(feature = "v2", feature = "tokenization_v2"))]
#[instrument(skip_all)]
pub async fn retrieve_value_from_vault(
    state: &routes::SessionState,
    request: pm_types::VaultRetrieveRequest,
) -> CustomResult<serde_json::value::Value, errors::VaultError> {
    let payload = request
        .encode_to_vec()
        .change_context(errors::VaultError::RequestEncodingFailed)
        .attach_printable("Failed to encode VaultRetrieveRequest")?;

    let resp = call_to_vault::<pm_types::VaultRetrieve>(state, payload)
        .await
        .change_context(errors::VaultError::VaultAPIError)
        .attach_printable("Call to vault failed")?;

    let stored_resp: serde_json::Value = resp
        .parse_struct("VaultRetrieveResponse")
        .change_context(errors::VaultError::ResponseDeserializationFailed)
        .attach_printable("Failed to parse data into VaultRetrieveResponse")?;

    Ok(stored_resp)
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn retrieve_payment_method_from_vault_external(
    state: &routes::SessionState,
    merchant_account: &domain::MerchantAccount,
    pm: &domain::PaymentMethod,
    merchant_connector_account: domain::MerchantConnectorAccountTypeDetails,
) -> RouterResult<pm_types::VaultRetrieveResponse> {
    let connector_vault_id = pm
        .locker_id
        .clone()
        .map(|id| id.get_string_repr().to_owned());

    let merchant_connector_account = match &merchant_connector_account {
        domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(mca) => {
            Ok(mca.as_ref())
        }
        domain::MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(_) => {
            Err(report!(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("MerchantConnectorDetails not supported for vault operations"))
        }
    }?;

    let router_data = core_utils::construct_vault_router_data(
        state,
        merchant_account.get_id(),
        merchant_connector_account,
        None,
        connector_vault_id,
        None,
    )
    .await?;

    let mut old_router_data = VaultConnectorFlowData::to_old_router_data(router_data)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable(
            "Cannot construct router data for making the external vault retrieve api call",
        )?;

    let connector_name = merchant_connector_account.get_connector_name_as_string(); // always get the connector name from this call

    let connector_data = api::ConnectorData::get_external_vault_connector_by_name(
        &state.conf.connectors,
        connector_name,
        api::GetToken::Connector,
        Some(merchant_connector_account.get_id()),
    )
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to get the connector data")?;

    let connector_integration: services::BoxedVaultConnectorIntegrationInterface<
        ExternalVaultRetrieveFlow,
        types::VaultRequestData,
        types::VaultResponseData,
    > = connector_data.connector.get_connector_integration();

    let router_data_resp = services::execute_connector_processing_step(
        state,
        connector_integration,
        &old_router_data,
        payments_core::CallConnectorAction::Trigger,
        None,
        None,
    )
    .await
    .to_vault_failed_response()?;

    get_vault_response_for_retrieve_payment_method_data::<ExternalVaultRetrieveFlow>(
        router_data_resp,
    )
}

#[cfg(feature = "v2")]
pub fn get_vault_response_for_retrieve_payment_method_data<F>(
    router_data: VaultRouterData<F>,
) -> RouterResult<pm_types::VaultRetrieveResponse> {
    match router_data.response {
        Ok(response) => match response {
            types::VaultResponseData::ExternalVaultRetrieveResponse { vault_data } => {
                Ok(pm_types::VaultRetrieveResponse { data: vault_data })
            }
            types::VaultResponseData::ExternalVaultInsertResponse { .. }
            | types::VaultResponseData::ExternalVaultDeleteResponse { .. }
            | types::VaultResponseData::ExternalVaultCreateResponse { .. } => {
                Err(report!(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Invalid Vault Response"))
            }
        },
        Err(err) => Err(report!(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to retrieve payment method")),
    }
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn retrieve_payment_method_from_vault_using_payment_token(
    state: &routes::SessionState,
    merchant_context: &domain::MerchantContext,
    profile: &domain::Profile,
    payment_token: &String,
    payment_method_type: &common_enums::PaymentMethod,
) -> RouterResult<(domain::PaymentMethod, domain::PaymentMethodVaultingData)> {
    let pm_token_data = utils::retrieve_payment_token_data(
        state,
        payment_token.to_string(),
        Some(payment_method_type),
    )
    .await?;

    let payment_method_id = match pm_token_data {
        storage::PaymentTokenData::PermanentCard(card_token_data) => {
            card_token_data.payment_method_id
        }
        storage::PaymentTokenData::TemporaryGeneric(_) => {
            Err(errors::ApiErrorResponse::NotImplemented {
                message: errors::NotImplementedMessage::Reason(
                    "TemporaryGeneric Token not implemented".to_string(),
                ),
            })?
        }
        storage::PaymentTokenData::AuthBankDebit(_) => {
            Err(errors::ApiErrorResponse::NotImplemented {
                message: errors::NotImplementedMessage::Reason(
                    "AuthBankDebit Token not implemented".to_string(),
                ),
            })?
        }
    };
    let db = &*state.store;
    let key_manager_state = &state.into();

    let storage_scheme = merchant_context.get_merchant_account().storage_scheme;

    let payment_method = db
        .find_payment_method(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            &payment_method_id,
            storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

    let vault_data =
        retrieve_payment_method_from_vault(state, merchant_context, profile, &payment_method)
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to retrieve payment method from vault")?
            .data;

    Ok((payment_method, vault_data))
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TemporaryVaultCvc {
    card_cvc: masking::Secret<String>,
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn insert_cvc_using_payment_token(
    state: &routes::SessionState,
    payment_token: &String,
    payment_method_data: api_models::payment_methods::PaymentMethodCreateData,
    payment_method: common_enums::PaymentMethod,
    fulfillment_time: i64,
    encryption_key: &masking::Secret<Vec<u8>>,
) -> RouterResult<()> {
    let card_cvc = domain::PaymentMethodVaultingData::try_from(payment_method_data)?
        .get_card()
        .and_then(|card| card.card_cvc.clone());

    if let Some(card_cvc) = card_cvc {
        let redis_conn = state
            .store
            .get_redis_conn()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to get redis connection")?;

        let key = format!("pm_token_{payment_token}_{payment_method}_hyperswitch_cvc");

        let payload_to_be_encrypted = TemporaryVaultCvc { card_cvc };

        let payload = payload_to_be_encrypted
            .encode_to_string_of_json()
            .change_context(errors::ApiErrorResponse::InternalServerError)?;

        // Encrypt the CVC and store it in Redis
        let encrypted_payload = GcmAes256
            .encode_message(encryption_key.peek().as_ref(), payload.as_bytes())
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to encode TemporaryVaultCvc for vault")?;

        redis_conn
            .set_key_if_not_exists_with_expiry(
                &key.as_str().into(),
                bytes::Bytes::from(encrypted_payload),
                Some(fulfillment_time),
            )
            .await
            .change_context(errors::StorageError::KVError)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to add token in redis")?;
    };

    Ok(())
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn retrieve_and_delete_cvc_from_payment_token(
    state: &routes::SessionState,
    payment_token: &String,
    payment_method: common_enums::PaymentMethod,
    encryption_key: &masking::Secret<Vec<u8>>,
) -> RouterResult<masking::Secret<String>> {
    let redis_conn = state
        .store
        .get_redis_conn()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to get redis connection")?;

    let key = format!("pm_token_{payment_token}_{payment_method}_hyperswitch_cvc",);

    let data = redis_conn
        .get_key::<bytes::Bytes>(&key.clone().into())
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to fetch the token from redis")?;

    // decrypt the cvc data
    let decrypted_payload = GcmAes256
        .decode_message(
            encryption_key.peek().as_ref(),
            masking::Secret::new(data.into()),
        )
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to decode TemporaryVaultCvc from vault")?;

    let cvc_data: TemporaryVaultCvc = bytes::Bytes::from(decrypted_payload)
        .parse_struct("TemporaryVaultCvc")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to deserialize TemporaryVaultCvc")?;

    // delete key after retrieving the cvc
    redis_conn.delete_key(&key.into()).await.map_err(|err| {
        logger::error!("Failed to delete token from redis: {:?}", err);
    });

    Ok(cvc_data.card_cvc)
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn delete_payment_token(
    state: &routes::SessionState,
    key_for_token: &str,
    intent_status: enums::IntentStatus,
) -> RouterResult<()> {
    if ![
        enums::IntentStatus::RequiresCustomerAction,
        enums::IntentStatus::RequiresMerchantAction,
    ]
    .contains(&intent_status)
    {
        utils::delete_payment_token_data(state, key_for_token)
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to delete payment_token")?;
    }
    Ok(())
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn retrieve_payment_method_from_vault(
    state: &routes::SessionState,
    merchant_context: &domain::MerchantContext,
    profile: &domain::Profile,
    pm: &domain::PaymentMethod,
) -> RouterResult<pm_types::VaultRetrieveResponse> {
    let is_external_vault_enabled = profile.is_external_vault_enabled();

    match is_external_vault_enabled {
        true => {
            let external_vault_source = pm.external_vault_source.as_ref();

            let merchant_connector_account =
                domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(Box::new(
                    payments_core::helpers::get_merchant_connector_account_v2(
                        state,
                        merchant_context.get_merchant_key_store(),
                        external_vault_source,
                    )
                    .await
                    .attach_printable(
                        "failed to fetch merchant connector account for external vault retrieve",
                    )?,
                ));

            retrieve_payment_method_from_vault_external(
                state,
                merchant_context.get_merchant_account(),
                pm,
                merchant_connector_account,
            )
            .await
        }
        false => {
            let vault_id = pm
                .locker_id
                .clone()
                .ok_or(errors::VaultError::MissingRequiredField {
                    field_name: "locker_id",
                })
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Missing locker_id for VaultRetrieveRequest")?;
            retrieve_payment_method_from_vault_internal(
                state,
                merchant_context,
                &vault_id,
                &pm.customer_id,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to retrieve payment method from vault")
        }
    }
}

#[cfg(feature = "v2")]
pub async fn delete_payment_method_data_from_vault_internal(
    state: &routes::SessionState,
    merchant_context: &domain::MerchantContext,
    vault_id: domain::VaultId,
    customer_id: &id_type::GlobalCustomerId,
) -> CustomResult<pm_types::VaultDeleteResponse, errors::VaultError> {
    let payload = pm_types::VaultDeleteRequest {
        entity_id: customer_id.to_owned(),
        vault_id,
    }
    .encode_to_vec()
    .change_context(errors::VaultError::RequestEncodingFailed)
    .attach_printable("Failed to encode VaultDeleteRequest")?;

    let resp = call_to_vault::<pm_types::VaultDelete>(state, payload)
        .await
        .change_context(errors::VaultError::VaultAPIError)
        .attach_printable("Call to vault failed")?;

    let stored_pm_resp: pm_types::VaultDeleteResponse = resp
        .parse_struct("VaultDeleteResponse")
        .change_context(errors::VaultError::ResponseDeserializationFailed)
        .attach_printable("Failed to parse data into VaultDeleteResponse")?;

    Ok(stored_pm_resp)
}

#[cfg(feature = "v2")]
pub async fn delete_payment_method_data_from_vault_external(
    state: &routes::SessionState,
    merchant_account: &domain::MerchantAccount,
    merchant_connector_account: domain::MerchantConnectorAccountTypeDetails,
    vault_id: domain::VaultId,
    customer_id: &id_type::GlobalCustomerId,
) -> RouterResult<pm_types::VaultDeleteResponse> {
    let connector_vault_id = vault_id.get_string_repr().to_owned();

    // Extract MerchantConnectorAccount from the enum
    let merchant_connector_account = match &merchant_connector_account {
        domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(mca) => {
            Ok(mca.as_ref())
        }
        domain::MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(_) => {
            Err(report!(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("MerchantConnectorDetails not supported for vault operations"))
        }
    }?;

    let router_data = core_utils::construct_vault_router_data(
        state,
        merchant_account.get_id(),
        merchant_connector_account,
        None,
        Some(connector_vault_id),
        None,
    )
    .await?;

    let mut old_router_data = VaultConnectorFlowData::to_old_router_data(router_data)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable(
            "Cannot construct router data for making the external vault delete api call",
        )?;

    let connector_name = merchant_connector_account.get_connector_name_as_string(); // always get the connector name from this call

    let connector_data = api::ConnectorData::get_external_vault_connector_by_name(
        &state.conf.connectors,
        connector_name,
        api::GetToken::Connector,
        Some(merchant_connector_account.get_id()),
    )
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to get the connector data")?;

    let connector_integration: services::BoxedVaultConnectorIntegrationInterface<
        ExternalVaultDeleteFlow,
        types::VaultRequestData,
        types::VaultResponseData,
    > = connector_data.connector.get_connector_integration();

    let router_data_resp = services::execute_connector_processing_step(
        state,
        connector_integration,
        &old_router_data,
        payments_core::CallConnectorAction::Trigger,
        None,
        None,
    )
    .await
    .to_vault_failed_response()?;

    get_vault_response_for_delete_payment_method_data::<ExternalVaultDeleteFlow>(
        router_data_resp,
        customer_id.to_owned(),
    )
}

#[cfg(feature = "v2")]
pub fn get_vault_response_for_delete_payment_method_data<F>(
    router_data: VaultRouterData<F>,
    customer_id: id_type::GlobalCustomerId,
) -> RouterResult<pm_types::VaultDeleteResponse> {
    match router_data.response {
        Ok(response) => match response {
            types::VaultResponseData::ExternalVaultDeleteResponse { connector_vault_id } => {
                Ok(pm_types::VaultDeleteResponse {
                    vault_id: domain::VaultId::generate(connector_vault_id), // converted to VaultId type
                    entity_id: customer_id,
                })
            }
            types::VaultResponseData::ExternalVaultInsertResponse { .. }
            | types::VaultResponseData::ExternalVaultRetrieveResponse { .. }
            | types::VaultResponseData::ExternalVaultCreateResponse { .. } => {
                Err(report!(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Invalid Vault Response"))
            }
        },
        Err(err) => Err(report!(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to retrieve payment method")),
    }
}

#[cfg(feature = "v2")]
pub async fn delete_payment_method_data_from_vault(
    state: &routes::SessionState,
    merchant_context: &domain::MerchantContext,
    profile: &domain::Profile,
    pm: &domain::PaymentMethod,
) -> RouterResult<pm_types::VaultDeleteResponse> {
    let is_external_vault_enabled = profile.is_external_vault_enabled();

    let vault_id = pm
        .locker_id
        .clone()
        .get_required_value("locker_id")
        .attach_printable("Missing locker_id in PaymentMethod")?;

    match is_external_vault_enabled {
        true => {
            let external_vault_source = pm.external_vault_source.as_ref();

            let merchant_connector_account =
                domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(Box::new(
                    payments_core::helpers::get_merchant_connector_account_v2(
                        state,
                        merchant_context.get_merchant_key_store(),
                        external_vault_source,
                    )
                    .await
                    .attach_printable(
                        "failed to fetch merchant connector account for external vault delete",
                    )?,
                ));

            delete_payment_method_data_from_vault_external(
                state,
                merchant_context.get_merchant_account(),
                merchant_connector_account,
                vault_id.clone(),
                &pm.customer_id,
            )
            .await
        }
        false => delete_payment_method_data_from_vault_internal(
            state,
            merchant_context,
            vault_id,
            &pm.customer_id,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to delete payment method from vault"),
    }
}

#[cfg(feature = "v1")]
#[instrument(skip_all)]
pub async fn retrieve_payment_method_from_vault_external_v1(
    state: &routes::SessionState,
    merchant_id: &id_type::MerchantId,
    pm: &domain::PaymentMethod,
    merchant_connector_account: hyperswitch_domain_models::merchant_connector_account::MerchantConnectorAccount,
) -> RouterResult<hyperswitch_domain_models::vault::PaymentMethodVaultingData> {
    let connector_vault_id = pm.locker_id.clone().map(|id| id.to_string());

    let router_data = core_utils::construct_vault_router_data(
        state,
        merchant_id,
        &merchant_connector_account,
        None,
        connector_vault_id,
        None,
    )
    .await?;

    let old_router_data = VaultConnectorFlowData::to_old_router_data(router_data)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable(
            "Cannot construct router data for making the external vault retrieve api call",
        )?;

    let connector_name = merchant_connector_account.get_connector_name_as_string();

    let connector_data = api::ConnectorData::get_external_vault_connector_by_name(
        &state.conf.connectors,
        connector_name,
        api::GetToken::Connector,
        Some(merchant_connector_account.get_id()),
    )
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to get the connector data")?;

    let connector_integration: services::BoxedVaultConnectorIntegrationInterface<
        hyperswitch_domain_models::router_flow_types::ExternalVaultRetrieveFlow,
        types::VaultRequestData,
        types::VaultResponseData,
    > = connector_data.connector.get_connector_integration();

    let router_data_resp = services::execute_connector_processing_step(
        state,
        connector_integration,
        &old_router_data,
        payments::CallConnectorAction::Trigger,
        None,
        None,
    )
    .await
    .to_vault_failed_response()?;

    get_vault_response_for_retrieve_payment_method_data_v1(router_data_resp)
}

pub fn get_vault_response_for_retrieve_payment_method_data_v1<F>(
    router_data: VaultRouterData<F>,
) -> RouterResult<hyperswitch_domain_models::vault::PaymentMethodVaultingData> {
    match router_data.response {
        Ok(response) => match response {
            types::VaultResponseData::ExternalVaultRetrieveResponse { vault_data } => {
                Ok(vault_data)
            }
            types::VaultResponseData::ExternalVaultInsertResponse { .. }
            | types::VaultResponseData::ExternalVaultDeleteResponse { .. }
            | types::VaultResponseData::ExternalVaultCreateResponse { .. } => {
                Err(report!(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Invalid Vault Response"))
            }
        },
        Err(err) => {
            logger::error!(
                "Failed to retrieve payment method from external vault: {:?}",
                err
            );
            Err(report!(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to retrieve payment method from external vault"))
        }
    }
}

// ********************************************** PROCESS TRACKER **********************************************

pub async fn add_delete_tokenized_data_task(
    db: &dyn db::StorageInterface,
    lookup_key: &str,
    pm: enums::PaymentMethod,
) -> RouterResult<()> {
    let runner = storage::ProcessTrackerRunner::DeleteTokenizeDataWorkflow;
    let process_tracker_id = format!("{runner}_{lookup_key}");
    let task = runner.to_string();
    let tag = ["BASILISK-V3"];
    let tracking_data = storage::TokenizeCoreWorkflow {
        lookup_key: lookup_key.to_owned(),


// Struct: BreadpayRouterData
// File: crates/hyperswitch_connectors/src/connectors/breadpay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BreadpayRouterData<T>


        utils::flatten_join_error(payment_attempt_fut)
    )?;

    #[cfg(all(feature = "v1", feature = "dynamic_routing"))]
    {
        if payment_intent.status.is_in_terminal_state()
            && business_profile.dynamic_routing_algorithm.is_some()
        {
            let dynamic_routing_algo_ref: api_models::routing::DynamicRoutingAlgorithmRef =
                business_profile
                    .dynamic_routing_algorithm
                    .clone()
                    .map(|val| val.parse_value("DynamicRoutingAlgorithmRef"))
                    .transpose()
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("unable to deserialize DynamicRoutingAlgorithmRef from JSON")?
                    .ok_or(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("DynamicRoutingAlgorithmRef not found in profile")?;

            let state = state.clone();
            let profile_id = business_profile.get_id().to_owned();
            let payment_attempt = payment_attempt.clone();
            let dynamic_routing_config_params_interpolator =
                routing_helpers::DynamicRoutingConfigParamsInterpolator::new(
                    payment_attempt.payment_method,
                    payment_attempt.payment_method_type,
                    payment_attempt.authentication_type,
                    payment_attempt.currency,
                    payment_data
                        .address
                        .get_payment_billing()
                        .and_then(|address| address.clone().address)
                        .and_then(|address| address.country),
                    payment_attempt
                        .payment_method_data
                        .as_ref()
                        .and_then(|data| data.as_object())
                        .and_then(|card| card.get("card"))
                        .and_then(|data| data.as_object())
                        .and_then(|card| card.get("card_network"))
                        .and_then(|network| network.as_str())
                        .map(|network| network.to_string()),
                    payment_attempt
                        .payment_method_data
                        .as_ref()
                        .and_then(|data| data.as_object())
                        .and_then(|card| card.get("card"))
                        .and_then(|data| data.as_object())
                        .and_then(|card| card.get("card_isin"))
                        .and_then(|card_isin| card_isin.as_str())
                        .map(|card_isin| card_isin.to_string()),
                );
            tokio::spawn(
                async move {
                    let should_route_to_open_router =
                        state.conf.open_router.dynamic_routing_enabled;
                    let is_success_rate_based = matches!(
                        payment_attempt.routing_approach,
                        Some(enums::RoutingApproach::SuccessRateExploitation)
                            | Some(enums::RoutingApproach::SuccessRateExploration)
                    );

                    if should_route_to_open_router && is_success_rate_based {
                        routing_helpers::update_gateway_score_helper_with_open_router(
                            &state,
                            &payment_attempt,
                            &profile_id,
                            dynamic_routing_algo_ref.clone(),
                        )
                        .await
                        .map_err(|e| logger::error!(open_router_update_gateway_score_err=?e))
                        .ok();
                    } else {
                        routing_helpers::push_metrics_with_update_window_for_success_based_routing(
                            &state,
                            &payment_attempt,
                            routable_connectors.clone(),
                            &profile_id,
                            dynamic_routing_algo_ref.clone(),
                            dynamic_routing_config_params_interpolator.clone(),
                        )
                        .await
                        .map_err(|e| logger::error!(success_based_routing_metrics_error=?e))
                        .ok();

                        if let Some(gsm_error_category) = gsm_error_category {
                            if gsm_error_category.should_perform_elimination_routing() {
                                logger::info!("Performing update window for elimination routing");
                                routing_helpers::update_window_for_elimination_routing(
                                    &state,
                                    &payment_attempt,
                                    &profile_id,
                                    dynamic_routing_algo_ref.clone(),
                                    dynamic_routing_config_params_interpolator.clone(),
                                    gsm_error_category,
                                )
                                .await
                                .map_err(|e| logger::error!(dynamic_routing_metrics_error=?e))
                                .ok();
                            };
                        };

                        routing_helpers::push_metrics_with_update_window_for_contract_based_routing(
                        &state,
                        &payment_attempt,
                        routable_connectors,
                        &profile_id,
                        dynamic_routing_algo_ref,
                        dynamic_routing_config_params_interpolator,
                    )
                    .await
                    .map_err(|e| logger::error!(contract_based_routing_metrics_error=?e))
                    .ok();
                    }
                }
                .in_current_span(),
            );
        }
    }

    payment_data.payment_intent = payment_intent;
    payment_data.payment_attempt = payment_attempt;
    router_data.payment_method_status.and_then(|status| {
        payment_data
            .payment_method_info
            .as_mut()
            .map(|info| info.status = status)
    });

    if payment_data.payment_attempt.status == enums::AttemptStatus::Failure {
        let _ = card_testing_guard_utils::increment_blocked_count_in_cache(
            state,
            payment_data.card_testing_guard_data.clone(),
        )
        .await;
    }

    match router_data.integrity_check {
        Ok(()) => Ok(payment_data),
        Err(err) => {
            metrics::INTEGRITY_CHECK_FAILED.add(
                1,
                router_env::metric_attributes!(
                    (
                        "connector",
                        payment_data
                            .payment_attempt
                            .connector
                            .clone()
                            .unwrap_or_default(),
                    ),
                    (
                        "merchant_id",
                        payment_data.payment_attempt.merchant_id.clone(),
                    )
                ),
            );
            Err(error_stack::Report::new(
                errors::ApiErrorResponse::IntegrityCheckFailed {
                    connector_transaction_id: payment_data
                        .payment_attempt
                        .get_connector_payment_id()
                        .map(ToString::to_string),
                    reason: payment_data
                        .payment_attempt
                        .error_message
                        .unwrap_or_default(),
                    field_names: err.field_names,
                },
            ))
        }
    }
}

#[cfg(feature = "v2")]
async fn update_payment_method_status_and_ntid<F: Clone>(
    state: &SessionState,
    key_store: &domain::MerchantKeyStore,
    payment_data: &mut PaymentData<F>,
    attempt_status: common_enums::AttemptStatus,
    payment_response: Result<types::PaymentsResponseData, ErrorResponse>,
    storage_scheme: enums::MerchantStorageScheme,
) -> RouterResult<()> {
    todo!()
}

#[cfg(feature = "v1")]
async fn update_payment_method_status_and_ntid<F: Clone>(
    state: &SessionState,
    key_store: &domain::MerchantKeyStore,
    payment_data: &mut PaymentData<F>,
    attempt_status: common_enums::AttemptStatus,
    payment_response: Result<types::PaymentsResponseData, ErrorResponse>,
    storage_scheme: enums::MerchantStorageScheme,
) -> RouterResult<()> {
    // If the payment_method is deleted then ignore the error related to retrieving payment method
    // This should be handled when the payment method is soft deleted
    if let Some(id) = &payment_data.payment_attempt.payment_method_id {
        let payment_method = match state
            .store
            .find_payment_method(&(state.into()), key_store, id, storage_scheme)
            .await
        {
            Ok(payment_method) => payment_method,
            Err(error) => {
                if error.current_context().is_db_not_found() {
                    logger::info!(
                        "Payment Method not found in db and skipping payment method update {:?}",
                        error
                    );
                    return Ok(());
                } else {
                    Err(error)
                            .change_context(errors::ApiErrorResponse::InternalServerError)
                            .attach_printable("Error retrieving payment method from db in update_payment_method_status_and_ntid")?
                }
            }
        };

        let pm_resp_network_transaction_id = payment_response
            .map(|resp| if let types::PaymentsResponseData::TransactionResponse { network_txn_id: network_transaction_id, .. } = resp {
                network_transaction_id
    } else {None})
    .map_err(|err| {
        logger::error!(error=?err, "Failed to obtain the network_transaction_id from payment response");
    })
    .ok()
    .flatten();
        let network_transaction_id = if payment_data.payment_intent.setup_future_usage
            == Some(diesel_models::enums::FutureUsage::OffSession)
        {
            if pm_resp_network_transaction_id.is_some() {
                pm_resp_network_transaction_id
            } else {
                logger::info!("Skip storing network transaction id");
                None
            }
        } else {
            None
        };

        let pm_update = if payment_method.status != common_enums::PaymentMethodStatus::Active
            && payment_method.status != attempt_status.into()
        {
            let updated_pm_status = common_enums::PaymentMethodStatus::from(attempt_status);
            payment_data
                .payment_method_info
                .as_mut()
                .map(|info| info.status = updated_pm_status);
            storage::PaymentMethodUpdate::NetworkTransactionIdAndStatusUpdate {
                network_transaction_id,
                status: Some(updated_pm_status),
            }
        } else {
            storage::PaymentMethodUpdate::NetworkTransactionIdAndStatusUpdate {
                network_transaction_id,
                status: None,
            }
        };

        state
            .store
            .update_payment_method(
                &(state.into()),
                key_store,
                payment_method,
                pm_update,
                storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to update payment method in db")?;
    };
    Ok(())
}

#[cfg(feature = "v2")]
impl<F: Send + Clone> Operation<F, types::PaymentsAuthorizeData> for &PaymentResponse {
    type Data = PaymentConfirmData<F>;
    fn to_post_update_tracker(
        &self,
    ) -> RouterResult<
        &(dyn PostUpdateTracker<F, Self::Data, types::PaymentsAuthorizeData> + Send + Sync),
    > {
        Ok(*self)
    }
}

#[cfg(feature = "v2")]
impl<F: Send + Clone> Operation<F, types::PaymentsAuthorizeData> for PaymentResponse {
    type Data = PaymentConfirmData<F>;
    fn to_post_update_tracker(
        &self,
    ) -> RouterResult<
        &(dyn PostUpdateTracker<F, Self::Data, types::PaymentsAuthorizeData> + Send + Sync),
    > {
        Ok(self)
    }
}

#[cfg(feature = "v2")]
impl<F: Send + Clone> Operation<F, types::PaymentsCaptureData> for PaymentResponse {
    type Data = hyperswitch_domain_models::payments::PaymentCaptureData<F>;
    fn to_post_update_tracker(
        &self,
    ) -> RouterResult<
        &(dyn PostUpdateTracker<F, Self::Data, types::PaymentsCaptureData> + Send + Sync),
    > {
        Ok(self)
    }
}

#[cfg(feature = "v2")]
#[async_trait]
impl<F: Clone>
    PostUpdateTracker<
        F,
        hyperswitch_domain_models::payments::PaymentCaptureData<F>,
        types::PaymentsCaptureData,
    > for PaymentResponse
{
    async fn update_tracker<'b>(
        &'b self,
        state: &'b SessionState,
        mut payment_data: hyperswitch_domain_models::payments::PaymentCaptureData<F>,
        response: types::RouterData<F, types::PaymentsCaptureData, types::PaymentsResponseData>,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> RouterResult<hyperswitch_domain_models::payments::PaymentCaptureData<F>>
    where
        F: 'b + Send + Sync,
        types::RouterData<F, types::PaymentsCaptureData, types::PaymentsResponseData>:
            hyperswitch_domain_models::router_data::TrackerPostUpdateObjects<
                F,
                types::PaymentsCaptureData,
                hyperswitch_domain_models::payments::PaymentCaptureData<F>,
            >,
    {
        use hyperswitch_domain_models::router_data::TrackerPostUpdateObjects;

        let db = &*state.store;
        let key_manager_state = &state.into();

        let response_router_data = response;

        let payment_intent_update =
            response_router_data.get_payment_intent_update(&payment_data, storage_scheme);

        let payment_attempt_update =
            response_router_data.get_payment_attempt_update(&payment_data, storage_scheme);

        let updated_payment_intent = db
            .update_payment_intent(
                key_manager_state,
                payment_data.payment_intent,
                payment_intent_update,
                key_store,
                storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to update payment intent")?;

        let updated_payment_attempt = db
            .update_payment_attempt(
                key_manager_state,
                key_store,
                payment_data.payment_attempt,
                payment_attempt_update,
                storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to update payment attempt")?;

        payment_data.payment_intent = updated_payment_intent;
        payment_data.payment_attempt = updated_payment_attempt;

        Ok(payment_data)
    }
}

#[cfg(feature = "v2")]
#[async_trait]
impl<F: Clone> PostUpdateTracker<F, PaymentConfirmData<F>, types::PaymentsAuthorizeData>
    for PaymentResponse
{
    async fn update_tracker<'b>(
        &'b self,
        state: &'b SessionState,
        mut payment_data: PaymentConfirmData<F>,
        response: types::RouterData<F, types::PaymentsAuthorizeData, types::PaymentsResponseData>,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> RouterResult<PaymentConfirmData<F>>
    where
        F: 'b + Send + Sync,
        types::RouterData<F, types::PaymentsAuthorizeData, types::PaymentsResponseData>:
            hyperswitch_domain_models::router_data::TrackerPostUpdateObjects<
                F,
                types::PaymentsAuthorizeData,
                PaymentConfirmData<F>,
            >,
    {
        use hyperswitch_domain_models::router_data::TrackerPostUpdateObjects;

        let db = &*state.store;
        let key_manager_state = &state.into();

        let response_router_data = response;

        let payment_intent_update =
            response_router_data.get_payment_intent_update(&payment_data, storage_scheme);
        let payment_attempt_update =
            response_router_data.get_payment_attempt_update(&payment_data, storage_scheme);

        let updated_payment_intent = db
            .update_payment_intent(
                key_manager_state,
                payment_data.payment_intent,
                payment_intent_update,
                key_store,
                storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to update payment intent")?;

        let updated_payment_attempt = db
            .update_payment_attempt(
                key_manager_state,
                key_store,
                payment_data.payment_attempt,
                payment_attempt_update,
                storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to update payment attempt")?;

        let attempt_status = updated_payment_attempt.status;

        payment_data.payment_intent = updated_payment_intent;
        payment_data.payment_attempt = updated_payment_attempt;

        if let Some(payment_method) = &payment_data.payment_method {
            match attempt_status {
                common_enums::AttemptStatus::AuthenticationFailed
                | common_enums::AttemptStatus::RouterDeclined
                | common_enums::AttemptStatus::AuthorizationFailed
                | common_enums::AttemptStatus::Voided
                | common_enums::AttemptStatus::VoidedPostCharge
                | common_enums::AttemptStatus::VoidInitiated
                | common_enums::AttemptStatus::CaptureFailed
                | common_enums::AttemptStatus::VoidFailed
                | common_enums::AttemptStatus::AutoRefunded
                | common_enums::AttemptStatus::Unresolved
                | common_enums::AttemptStatus::Pending
                | common_enums::AttemptStatus::Failure
                | common_enums::AttemptStatus::Expired => (),

                common_enums::AttemptStatus::Started
                | common_enums::AttemptStatus::AuthenticationPending
                | common_enums::AttemptStatus::AuthenticationSuccessful
                | common_enums::AttemptStatus::Authorized
                | common_enums::AttemptStatus::PartiallyAuthorized
                | common_enums::AttemptStatus::Charged
                | common_enums::AttemptStatus::Authorizing
                | common_enums::AttemptStatus::CodInitiated
                | common_enums::AttemptStatus::PartialCharged
                | common_enums::AttemptStatus::PartialChargedAndChargeable
                | common_enums::AttemptStatus::CaptureInitiated
                | common_enums::AttemptStatus::PaymentMethodAwaited
                | common_enums::AttemptStatus::ConfirmationAwaited
                | common_enums::AttemptStatus::DeviceDataCollectionPending
                | common_enums::AttemptStatus::IntegrityFailure => {
                    let pm_update_status = enums::PaymentMethodStatus::Active;

                    // payment_methods microservice call
                    payment_methods::update_payment_method_status_internal(
                        state,
                        key_store,
                        storage_scheme,
                        pm_update_status,
                        payment_method.get_id(),
                    )
                    .await
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to update payment method status")?;
                }
            }
        }

        Ok(payment_data)
    }
}

#[cfg(feature = "v2")]
impl<F: Send + Clone> Operation<F, types::PaymentsSyncData> for PaymentResponse {
    type Data = PaymentStatusData<F>;
    fn to_post_update_tracker(
        &self,
    ) -> RouterResult<&(dyn PostUpdateTracker<F, Self::Data, types::PaymentsSyncData> + Send + Sync)>
    {
        Ok(self)
    }
}

#[cfg(feature = "v2")]
#[async_trait]
impl<F: Clone> PostUpdateTracker<F, PaymentStatusData<F>, types::PaymentsSyncData>
    for PaymentResponse
{
    async fn update_tracker<'b>(
        &'b self,
        state: &'b SessionState,
        mut payment_data: PaymentStatusData<F>,
        response: types::RouterData<F, types::PaymentsSyncData, types::PaymentsResponseData>,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> RouterResult<PaymentStatusData<F>>
    where
        F: 'b + Send + Sync,
        types::RouterData<F, types::PaymentsSyncData, types::PaymentsResponseData>:
            hyperswitch_domain_models::router_data::TrackerPostUpdateObjects<
                F,
                types::PaymentsSyncData,
                PaymentStatusData<F>,
            >,
    {
        use hyperswitch_domain_models::router_data::TrackerPostUpdateObjects;

        let db = &*state.store;
        let key_manager_state = &state.into();

        let response_router_data = response;

        let payment_intent_update =
            response_router_data.get_payment_intent_update(&payment_data, storage_scheme);
        let payment_attempt_update =
            response_router_data.get_payment_attempt_update(&payment_data, storage_scheme);

        let payment_attempt = payment_data.payment_attempt;

        let updated_payment_intent = db
            .update_payment_intent(
                key_manager_state,
                payment_data.payment_intent,
                payment_intent_update,
                key_store,
                storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to update payment intent")?;

        let updated_payment_attempt = db
            .update_payment_attempt(
                key_manager_state,
                key_store,
                payment_attempt,
                payment_attempt_update,
                storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to update payment attempt")?;

        payment_data.payment_intent = updated_payment_intent;
        payment_data.payment_attempt = updated_payment_attempt;

        Ok(payment_data)
    }
}

#[cfg(feature = "v2")]
impl<F: Send + Clone> Operation<F, types::SetupMandateRequestData> for &PaymentResponse {
    type Data = PaymentConfirmData<F>;
    fn to_post_update_tracker(
        &self,
    ) -> RouterResult<
        &(dyn PostUpdateTracker<F, Self::Data, types::SetupMandateRequestData> + Send + Sync),
    > {
        Ok(*self)
    }
}

#[cfg(feature = "v2")]
impl<F: Send + Clone> Operation<F, types::SetupMandateRequestData> for PaymentResponse {
    type Data = PaymentConfirmData<F>;
    fn to_post_update_tracker(
        &self,
    ) -> RouterResult<
        &(dyn PostUpdateTracker<F, Self::Data, types::SetupMandateRequestData> + Send + Sync),
    > {
        Ok(self)
    }
}

#[cfg(feature = "v2")]
impl
    Operation<
        hyperswitch_domain_models::router_flow_types::ExternalVaultProxy,
        hyperswitch_domain_models::router_request_types::ExternalVaultProxyPaymentsData,
    > for PaymentResponse
{
    type Data =
        PaymentConfirmData<hyperswitch_domain_models::router_flow_types::ExternalVaultProxy>;
    fn to_post_update_tracker(
        &self,
    ) -> RouterResult<
        &(dyn PostUpdateTracker<
            hyperswitch_domain_models::router_flow_types::ExternalVaultProxy,
            Self::Data,
            hyperswitch_domain_models::router_request_types::ExternalVaultProxyPaymentsData,
        > + Send
              + Sync),
    > {
        Ok(self)
    }
}

#[cfg(feature = "v2")]
impl
    Operation<
        hyperswitch_domain_models::router_flow_types::ExternalVaultProxy,
        hyperswitch_domain_models::router_request_types::ExternalVaultProxyPaymentsData,
    > for &PaymentResponse
{
    type Data =
        PaymentConfirmData<hyperswitch_domain_models::router_flow_types::ExternalVaultProxy>;
    fn to_post_update_tracker(
        &self,
    ) -> RouterResult<
        &(dyn PostUpdateTracker<
            hyperswitch_domain_models::router_flow_types::ExternalVaultProxy,
            Self::Data,
            hyperswitch_domain_models::router_request_types::ExternalVaultProxyPaymentsData,
        > + Send
              + Sync),
    > {
        Ok(*self)
    }
}

#[cfg(feature = "v2")]
#[async_trait]
impl
    PostUpdateTracker<
        hyperswitch_domain_models::router_flow_types::ExternalVaultProxy,
        PaymentConfirmData<hyperswitch_domain_models::router_flow_types::ExternalVaultProxy>,
        hyperswitch_domain_models::router_request_types::ExternalVaultProxyPaymentsData,
    > for PaymentResponse
{
    async fn update_tracker<'b>(
        &'b self,
        state: &'b SessionState,
        mut payment_data: PaymentConfirmData<
            hyperswitch_domain_models::router_flow_types::ExternalVaultProxy,
        >,
        response: types::RouterData<
            hyperswitch_domain_models::router_flow_types::ExternalVaultProxy,
            hyperswitch_domain_models::router_request_types::ExternalVaultProxyPaymentsData,
            types::PaymentsResponseData,
        >,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> RouterResult<
        PaymentConfirmData<hyperswitch_domain_models::router_flow_types::ExternalVaultProxy>,
    >
    where
        types::RouterData<
            hyperswitch_domain_models::router_flow_types::ExternalVaultProxy,
            hyperswitch_domain_models::router_request_types::ExternalVaultProxyPaymentsData,
            types::PaymentsResponseData,
        >: hyperswitch_domain_models::router_data::TrackerPostUpdateObjects<
            hyperswitch_domain_models::router_flow_types::ExternalVaultProxy,
            hyperswitch_domain_models::router_request_types::ExternalVaultProxyPaymentsData,
            PaymentConfirmData<hyperswitch_domain_models::router_flow_types::ExternalVaultProxy>,
        >,
    {
        use hyperswitch_domain_models::router_data::TrackerPostUpdateObjects;

        let db = &*state.store;
        let key_manager_state = &state.into();

        let response_router_data = response;

        let payment_intent_update =
            response_router_data.get_payment_intent_update(&payment_data, storage_scheme);
        let payment_attempt_update =
            response_router_data.get_payment_attempt_update(&payment_data, storage_scheme);

        let updated_payment_intent = db
            .update_payment_intent(
                key_manager_state,
                payment_data.payment_intent,
                payment_intent_update,
                key_store,
                storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to update payment intent")?;

        let updated_payment_attempt = db
            .update_payment_attempt(
                key_manager_state,
                key_store,
                payment_data.payment_attempt,
                payment_attempt_update,
                storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to update payment attempt")?;

        payment_data.payment_intent = updated_payment_intent;
        payment_data.payment_attempt = updated_payment_attempt;

        // TODO: Add external vault specific post-update logic if needed

        Ok(payment_data)
    }
}

#[cfg(feature = "v2")]
#[async_trait]
impl<F: Clone> PostUpdateTracker<F, PaymentConfirmData<F>, types::SetupMandateRequestData>
    for PaymentResponse
{
    async fn update_tracker<'b>(
        &'b self,
        state: &'b SessionState,
        mut payment_data: PaymentConfirmData<F>,
        response: types::RouterData<F, types::SetupMandateRequestData, types::PaymentsResponseData>,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> RouterResult<PaymentConfirmData<F>>
    where
        F: 'b + Send + Sync,
        types::RouterData<F, types::SetupMandateRequestData, types::PaymentsResponseData>:
            hyperswitch_domain_models::router_data::TrackerPostUpdateObjects<
                F,
                types::SetupMandateRequestData,
                PaymentConfirmData<F>,
            >,
    {
        use hyperswitch_domain_models::router_data::TrackerPostUpdateObjects;

        let db = &*state.store;
        let key_manager_state = &state.into();

        let response_router_data = response;

        let payment_intent_update =
            response_router_data.get_payment_intent_update(&payment_data, storage_scheme);
        let payment_attempt_update =
            response_router_data.get_payment_attempt_update(&payment_data, storage_scheme);

        let updated_payment_intent = db
            .update_payment_intent(
                key_manager_state,
                payment_data.payment_intent,
                payment_intent_update,
                key_store,
                storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to update payment intent")?;

        let updated_payment_attempt = db
            .update_payment_attempt(
                key_manager_state,
                key_store,
                payment_data.payment_attempt,
                payment_attempt_update,
                storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to update payment attempt")?;

        payment_data.payment_intent = updated_payment_intent;
        payment_data.payment_attempt = updated_payment_attempt;

        Ok(payment_data)
    }

    async fn save_pm_and_mandate<'b>(
        &self,
        state: &SessionState,
        router_data: &types::RouterData<
            F,
            types::SetupMandateRequestData,
            types::PaymentsResponseData,
        >,
        merchant_context: &domain::MerchantContext,
        payment_data: &mut PaymentConfirmData<F>,
        business_profile: &domain::Profile,
    ) -> CustomResult<(), errors::ApiErrorResponse>
    where
        F: 'b + Clone + Send + Sync,
    {
        // If we received a payment_method_id from connector in the router data response
        // Then we either update the payment method or create a new payment method
        // The case for updating the payment method is when the payment is created from the payment method service

        let Ok(payments_response) = &router_data.response else {
            // In case there was an error response from the connector
            // We do not take any action related to the payment method
            return Ok(());
        };

        let connector_request_reference_id = payment_data
            .payment_attempt
            .connector_token_details
            .as_ref()
            .and_then(|token_details| token_details.get_connector_token_request_reference_id());

        let connector_token =
            payments_response.get_updated_connector_token_details(connector_request_reference_id);

        let payment_method_id = payment_data.payment_attempt.payment_method_id.clone();

        // TODO: check what all conditions we will need to see if card need to be saved
        match (
            connector_token
                .as_ref()
                .and_then(|connector_token| connector_token.connector_mandate_id.clone()),
            payment_method_id,
        ) {
            (Some(token), Some(payment_method_id)) => {
                if !matches!(
                    router_data.status,
                    enums::AttemptStatus::Charged | enums::AttemptStatus::Authorized
                ) {
                    return Ok(());
                }
                let connector_id = payment_data
                    .payment_attempt
                    .merchant_connector_id
                    .clone()
                    .get_required_value("merchant_connector_id")
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("missing connector id")?;

                let net_amount = payment_data.payment_attempt.amount_details.get_net_amount();
                let currency = payment_data.payment_intent.amount_details.currency;

                let connector_token_details_for_payment_method_update =
                    api_models::payment_methods::ConnectorTokenDetails {
                        connector_id,
                        status: common_enums::ConnectorTokenStatus::Active,
                        connector_token_request_reference_id: connector_token
                            .and_then(|details| details.connector_token_request_reference_id),
                        original_payment_authorized_amount: Some(net_amount),
                        original_payment_authorized_currency: Some(currency),
                        metadata: None,
                        token: masking::Secret::new(token),
                        token_type: common_enums::TokenizationType::MultiUse,
                    };

                let payment_method_update_request =
                    api_models::payment_methods::PaymentMethodUpdate {
                        payment_method_data: None,
                        connector_token_details: Some(
                            connector_token_details_for_payment_method_update,
                        ),
                    };

                payment_methods::update_payment_method_core(
                    state,
                    merchant_context,
                    business_profile,
                    payment_method_update_request,
                    &payment_method_id,
                )
                .await
                .attach_printable("Failed to update payment method")?;
            }
            (Some(_), None) => {
                // TODO: create a new payment method
            }
            (None, Some(_)) | (None, None) => {}
        }

        Ok(())
    }
}

#[cfg(feature = "v1")]
fn update_connector_mandate_details_for_the_flow<F: Clone>(
    connector_mandate_id: Option<String>,
    mandate_metadata: Option<masking::Secret<serde_json::Value>>,
    connector_mandate_request_reference_id: Option<String>,
    payment_data: &mut PaymentData<F>,
) -> RouterResult<()> {
    let mut original_connector_mandate_reference_id = payment_data
        .payment_attempt
        .connector_mandate_detail
        .as_ref()
        .map(|detail| ConnectorMandateReferenceId::foreign_from(detail.clone()));
    let connector_mandate_reference_id = if connector_mandate_id.is_some() {
        if let Some(ref mut record) = original_connector_mandate_reference_id {
            record.update(
                connector_mandate_id,
                None,
                None,
                mandate_metadata,
                connector_mandate_request_reference_id,
            );
            Some(record.clone())
        } else {
            Some(ConnectorMandateReferenceId::new(
                connector_mandate_id,
                None,
                None,
                mandate_metadata,
                connector_mandate_request_reference_id,
            ))
        }
    } else {
        original_connector_mandate_reference_id
    };

    payment_data.payment_attempt.connector_mandate_detail = connector_mandate_reference_id
        .clone()
        .map(ForeignFrom::foreign_from);

    payment_data.set_mandate_id(api_models::payments::MandateIds {
        mandate_id: None,
        mandate_reference_id: connector_mandate_reference_id.map(|connector_mandate_id| {
            MandateReferenceId::ConnectorMandateId(connector_mandate_id)
        }),
    });
    Ok(())
}

fn response_to_capture_update(
    multiple_capture_data: &MultipleCaptureData,
    response_list: HashMap<String, CaptureSyncResponse>,
) -> RouterResult<Vec<(storage::Capture, storage::CaptureUpdate)>> {
    let mut capture_update_list = vec![];
    let mut unmapped_captures = vec![];
    for (connector_capture_id, capture_sync_response) in response_list {
        let capture =
            multiple_capture_data.get_capture_by_connector_capture_id(&connector_capture_id);
        if let Some(capture) = capture {
            capture_update_list.push((
                capture.clone(),
                storage::CaptureUpdate::foreign_try_from(capture_sync_response)?,
            ))
        } else {
            // connector_capture_id may not be populated in the captures table in some case
            // if so, we try to map the unmapped capture response and captures in DB.
            unmapped_captures.push(capture_sync_response)
        }
    }
    capture_update_list.extend(get_capture_update_for_unmapped_capture_responses(
        unmapped_captures,
        multiple_capture_data,
    )?);

    Ok(capture_update_list)
}

fn get_capture_update_for_unmapped_capture_responses(
    unmapped_capture_sync_response_list: Vec<CaptureSyncResponse>,
    multiple_capture_data: &MultipleCaptureData,
) -> RouterResult<Vec<(storage::Capture, storage::CaptureUpdate)>> {
    let mut result = Vec::new();
    let captures_without_connector_capture_id: Vec<_> = multiple_capture_data
        .get_pending_captures_without_connector_capture_id()
        .into_iter()
        .cloned()
        .collect();
    for capture_sync_response in unmapped_capture_sync_response_list {
        if let Some(capture) = captures_without_connector_capture_id
            .iter()
            .find(|capture| {
                capture_sync_response.get_connector_response_reference_id()
                    == Some(capture.capture_id.clone())
                    || capture_sync_response.get_amount_captured() == Some(capture.amount)
            })
        {
            result.push((
                capture.clone(),
                storage::CaptureUpdate::foreign_try_from(capture_sync_response)?,
            ))
        }
    }
    Ok(result)
}

fn get_total_amount_captured<F: Clone, T: types::Capturable>(
    request: &T,
    amount_captured: Option<MinorUnit>,
    router_data_status: enums::AttemptStatus,
    payment_data: &PaymentData<F>,
) -> Option<MinorUnit> {
    match &payment_data.multiple_capture_data {
        Some(multiple_capture_data) => {
            //multiple capture
            Some(multiple_capture_data.get_total_blocked_amount())
        }
        None => {
            //Non multiple capture
            let amount = request
                .get_captured_amount(
                    amount_captured.map(MinorUnit::get_amount_as_i64),
                    payment_data,
                )
                .map(MinorUnit::new);
            amount_captured.or_else(|| {
                if router_data_status == enums::AttemptStatus::Charged {
                    amount
                } else {
                    None
                }
            })
        }
    }
}

#[cfg(feature = "v2")]
impl<F: Send + Clone + Sync> Operation<F, types::PaymentsCancelData> for PaymentResponse {
    type Data = hyperswitch_domain_models::payments::PaymentCancelData<F>;
    fn to_post_update_tracker(
        &self,
    ) -> RouterResult<
        &(dyn PostUpdateTracker<F, Self::Data, types::PaymentsCancelData> + Send + Sync),
    > {
        Ok(self)
    }
}

#[cfg(feature = "v2")]
#[async_trait]
impl<F: Clone + Send + Sync>
    PostUpdateTracker<
        F,
        hyperswitch_domain_models::payments::PaymentCancelData<F>,
        types::PaymentsCancelData,
    > for PaymentResponse
{
    async fn update_tracker<'b>(
        &'b self,
        state: &'b SessionState,
        mut payment_data: hyperswitch_domain_models::payments::PaymentCancelData<F>,
        router_data: types::RouterData<F, types::PaymentsCancelData, types::PaymentsResponseData>,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> RouterResult<hyperswitch_domain_models::payments::PaymentCancelData<F>>
    where
        F: 'b + Send + Sync,
        types::RouterData<F, types::PaymentsCancelData, types::PaymentsResponseData>:
            hyperswitch_domain_models::router_data::TrackerPostUpdateObjects<
                F,
                types::PaymentsCancelData,
                hyperswitch_domain_models::payments::PaymentCancelData<F>,
            >,
    {
        let db = &*state.store;
        let key_manager_state = &state.into();

        use hyperswitch_domain_models::router_data::TrackerPostUpdateObjects;

        let payment_intent_update =
            router_data.get_payment_intent_update(&payment_data, storage_scheme);

        let updated_payment_intent = db
            .update_payment_intent(
                key_manager_state,
                payment_data.payment_intent.clone(),
                payment_intent_update,
                key_store,
                storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
            .attach_printable("Error while updating the payment_intent")?;

        let payment_attempt_update =
            router_data.get_payment_attempt_update(&payment_data, storage_scheme);

        let updated_payment_attempt = db
            .update_payment_attempt(
                key_manager_state,
                key_store,
                payment_data.payment_attempt.clone(),
                payment_attempt_update,
                storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
            .attach_printable("Error while updating the payment_attempt")?;

        payment_data.set_payment_intent(updated_payment_intent);
        payment_data.set_payment_attempt(updated_payment_attempt);

        Ok(payment_data)
    }
}


// Implementation: impl webhooks::IncomingWebhook for for Paystack
// File: crates/hyperswitch_connectors/src/connectors/paystack.rs
// Module: hyperswitch_connectors
// Methods: 6 total (0 public)

impl webhooks::IncomingWebhook for for Paystack


// Struct: PaypalSyncBatchResponse
// File: crates/hyperswitch_connectors/src/connectors/paypal/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PaypalSyncBatchResponse


// Function: new
// File: crates/router/src/events/api_logs.rs
// Module: router

pub fn new(
        tenant_id: common_utils::id_type::TenantId,
        merchant_id: Option<common_utils::id_type::MerchantId>,
        api_flow: &impl FlowMetric,
        request_id: &RequestId,
        latency: u128,
        status_code: i64,
        request: serde_json::Value,
        response: Option<serde_json::Value>,
        hs_latency: Option<u128>,
        auth_type: AuthenticationType,
        error: Option<serde_json::Value>,
        event_type: ApiEventsType,
        http_req: &HttpRequest,
        http_method: &http::Method,
        infra_components: Option<serde_json::Value>,
    ) -> Self