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

Code before:
            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)
                .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,
) -> CustomResult<pm_types::VaultDeleteResponse, errors::VaultError> {
    let payload = pm_types::VaultDeleteRequest {
        entity_id: merchant_context.get_merchant_account().get_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)
}

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

Code after:
            )
            .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_{}_{}_hyperswitch_cvc",
        payment_token, payment_method
    );

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