public CompletableFuture<Void> redeemReceipt(
      final Account account,
      final ReceiptCredentialPresentation receiptCredentialPresentation) {
    try {
      serverZkReceiptOperations.verifyReceiptCredentialPresentation(receiptCredentialPresentation);
    } catch (VerificationFailedException e) {
      throw Status.INVALID_ARGUMENT
          .withDescription("receipt credential presentation verification failed")
          .asRuntimeException();
    }
    final ReceiptSerial receiptSerial = receiptCredentialPresentation.getReceiptSerial();
    final Instant receiptExpiration = Instant.ofEpochSecond(receiptCredentialPresentation.getReceiptExpirationTime());
    if (clock.instant().isAfter(receiptExpiration)) {
      throw Status.INVALID_ARGUMENT.withDescription("receipt is already expired").asRuntimeException();
    }

    final long receiptLevel = receiptCredentialPresentation.getReceiptLevel();

    if (BackupLevelUtil.fromReceiptLevel(receiptLevel) != BackupLevel.MEDIA) {
      throw Status.INVALID_ARGUMENT
          .withDescription("server does not recognize the requested receipt level")
          .asRuntimeException();
    }

    return redeemedReceiptsManager
        .put(receiptSerial, receiptExpiration.getEpochSecond(), receiptLevel, account.getUuid())
        .thenCompose(receiptAllowed -> {
          if (!receiptAllowed) {
            throw Status.INVALID_ARGUMENT
                .withDescription("receipt serial is already redeemed")
                .asRuntimeException();
          }
          return accountsManager.updateAsync(account, a -> {
            final Account.BackupVoucher newPayment = new Account.BackupVoucher(receiptLevel, receiptExpiration);
            final Account.BackupVoucher existingPayment = a.getBackupVoucher();
            account.setBackupVoucher(merge(existingPayment, newPayment));
          });
        })
        .thenRun(Util.NOOP);
  }
@Test
  void redeemInvalidPresentation() throws InvalidInputException, VerificationFailedException {
    final BackupAuthManager authManager = create(BackupLevel.MESSAGES, false);
    final ReceiptCredentialPresentation invalid = receiptPresentation(ServerSecretParams.generate(), 3L, Instant.EPOCH);
    Assertions.assertThatExceptionOfType(StatusRuntimeException.class)
        .isThrownBy(() -> authManager.redeemReceipt(mock(Account.class), invalid).join())
        .extracting(ex -> ex.getStatus().getCode())
        .isEqualTo(Status.Code.INVALID_ARGUMENT);
    verifyNoInteractions(accountsManager);
    verifyNoInteractions(redeemedReceiptsManager);
  }