@POST
  @Path("/redeem-receipt")
  @Consumes(MediaType.APPLICATION_JSON)
  @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
  public CompletionStage<Response> redeemReceipt(
      @Mutable @Auth final AuthenticatedDevice auth,
      @NotNull @Valid final RedeemReceiptRequest request) {
    return CompletableFuture.supplyAsync(() -> {
      ReceiptCredentialPresentation receiptCredentialPresentation;
      try {
        receiptCredentialPresentation = receiptCredentialPresentationFactory.build(
            request.getReceiptCredentialPresentation());
      } catch (InvalidInputException e) {
        return CompletableFuture.completedFuture(Response.status(Status.BAD_REQUEST).entity("invalid receipt credential presentation").type(MediaType.TEXT_PLAIN_TYPE).build());
      }
      try {
        serverZkReceiptOperations.verifyReceiptCredentialPresentation(receiptCredentialPresentation);
      } catch (VerificationFailedException e) {
        return CompletableFuture.completedFuture(Response.status(Status.BAD_REQUEST).entity("receipt credential presentation verification failed").type(MediaType.TEXT_PLAIN_TYPE).build());
      }

      final ReceiptSerial receiptSerial = receiptCredentialPresentation.getReceiptSerial();
      final Instant receiptExpiration = Instant.ofEpochSecond(receiptCredentialPresentation.getReceiptExpirationTime());
      final long receiptLevel = receiptCredentialPresentation.getReceiptLevel();
      final String badgeId = badgesConfiguration.getReceiptLevels().get(receiptLevel);
      if (badgeId == null) {
        return CompletableFuture.completedFuture(Response.serverError().entity("server does not recognize the requested receipt level").type(MediaType.TEXT_PLAIN_TYPE).build());
      }
      return redeemedReceiptsManager.put(
              receiptSerial, receiptExpiration.getEpochSecond(), receiptLevel, auth.getAccount().getUuid())
          .thenCompose(receiptMatched -> {
        if (!receiptMatched) {
          return CompletableFuture.completedFuture(
              Response.status(Status.BAD_REQUEST).entity("receipt serial is already redeemed")
                  .type(MediaType.TEXT_PLAIN_TYPE).build());
        }

        return accountsManager.getByAccountIdentifierAsync(auth.getAccount().getUuid())
            .thenCompose(optionalAccount ->
                optionalAccount.map(account -> accountsManager.updateAsync(account, a -> {
                  a.addBadge(clock, new AccountBadge(badgeId, receiptExpiration, request.isVisible()));
                  if (request.isPrimary()) {
                    a.makeBadgePrimaryIfExists(clock, badgeId);
                  }
                })).orElse(CompletableFuture.completedFuture(null)))
            .thenApply(ignored -> Response.ok().build());
      });
    }).thenCompose(Function.identity());
  }
@Test
  void testRedeemReceipt() {
    when(receiptCredentialPresentation.getReceiptSerial()).thenReturn(receiptSerial);
    final long receiptLevel = 1L;
    when(receiptCredentialPresentation.getReceiptLevel()).thenReturn(receiptLevel);
    final long receiptExpiration = nowEpochSeconds + 86400 * 30;
    when(receiptCredentialPresentation.getReceiptExpirationTime()).thenReturn(receiptExpiration);
    when(redeemedReceiptsManager.put(same(receiptSerial), eq(receiptExpiration), eq(receiptLevel), eq(AuthHelper.VALID_UUID))).thenReturn(
        CompletableFuture.completedFuture(Boolean.TRUE));
    when(accountsManager.getByAccountIdentifierAsync(eq(AuthHelper.VALID_UUID))).thenReturn(
        CompletableFuture.completedFuture(Optional.of(AuthHelper.VALID_ACCOUNT)));

    RedeemReceiptRequest request = new RedeemReceiptRequest(presentation, true, true);
    Response response = resources.getJerseyTest()
        .target("/v1/donation/redeem-receipt")
        .request()
        .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
        .post(Entity.entity(request, MediaType.APPLICATION_JSON_TYPE));

    assertThat(response.getStatus()).isEqualTo(200);
    verify(AuthHelper.VALID_ACCOUNT).addBadge(same(clock), eq(new AccountBadge("TEST1", Instant.ofEpochSecond(receiptExpiration), true)));
    verify(AuthHelper.VALID_ACCOUNT).makeBadgePrimaryIfExists(same(clock), eq("TEST1"));
  }