Repository: juspay/hyperswitch
File: crates/router/src/core/payment_methods/vault.rs (Rust)

Code before:
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,

Review comment (suggestion):
```suggestion
            "pm_token_{}_{}_card_cvc",
```

Code after:
    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,
    fullfillment_time: i64,
    encryption_key: &masking::Secret<Vec<u8>>,
) -> RouterResult<()> {
    let card_cvc = domain::PaymentMethodVaultingData::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_{}_{}_hyperswitch_cvc",
            payment_token, payment_method
        );

        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(fullfillment_time),
            )
            .await
            .change_context(errors::StorageError::KVError)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to add token in redis")?;