Add docs to /v1/donations/redeem-receipt

This commit is contained in:
Ravi Khadiwala 2025-06-12 15:54:26 -05:00 committed by ravi-signal
parent 9a1da23bdb
commit 626a7fdad7
2 changed files with 38 additions and 10 deletions

View File

@ -6,6 +6,8 @@
package org.whispersystems.textsecuregcm.controllers;
import io.dropwizard.auth.Auth;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
@ -70,21 +72,40 @@ public class DonationController {
@Path("/redeem-receipt")
@Consumes(MediaType.APPLICATION_JSON)
@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
@Operation(
summary = "Redeem receipt",
description = """
Redeem a receipt acquired from /v1/subscription/{subscriberId}/receipt_credentials to add a badge to the
account. After successful redemption, profile responses will include the corresponding badge (if configured as
visible) until the expiration time on the receipt.
""")
@ApiResponse(responseCode = "200", description = "The receipt was redeemed")
@ApiResponse(responseCode = "400", description = """
The provided presentation or receipt was invalid, or the receipt was already redeemed for a different account. A
specific error message suitable for logging will be included as text/plain body
""")
@ApiResponse(responseCode = "429", description = "Rate limited.")
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());
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());
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());
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();
@ -92,16 +113,19 @@ public class DonationController {
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 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 CompletableFuture.completedFuture(Response.status(Status.BAD_REQUEST)
.entity("receipt serial is already redeemed")
.type(MediaType.TEXT_PLAIN_TYPE)
.build());
}
return accountsManager.updateAsync(auth.getAccount(), a -> {

View File

@ -7,12 +7,16 @@ package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
public class RedeemReceiptRequest {
@Schema(description = "Presentation of a ZK receipt encoded in standard padded base64", implementation = String.class)
private final byte[] receiptCredentialPresentation;
@Schema(description = "If true, the corresponding badge should be visible on the profile")
private final boolean visible;
@Schema(description = "if true, and the new badge is visible, it should be the primary badge on the profile")
private final boolean primary;
@JsonCreator