Record level on boost payment intent
This commit is contained in:
parent
578ea12b59
commit
63be7b93ce
|
@ -25,6 +25,7 @@ import java.math.BigDecimal;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.time.Clock;
|
import java.time.Clock;
|
||||||
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
@ -494,28 +495,9 @@ public class SubscriptionController {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class CreateBoostRequest {
|
public static class CreateBoostRequest {
|
||||||
|
@NotEmpty @ExactlySize(3) public String currency;
|
||||||
private final String currency;
|
@Min(1) public long amount;
|
||||||
private final long amount;
|
public Long level;
|
||||||
|
|
||||||
@JsonCreator
|
|
||||||
public CreateBoostRequest(
|
|
||||||
@JsonProperty("currency") String currency,
|
|
||||||
@JsonProperty("amount") long amount) {
|
|
||||||
this.currency = currency;
|
|
||||||
this.amount = amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotEmpty
|
|
||||||
@ExactlySize(3)
|
|
||||||
public String getCurrency() {
|
|
||||||
return currency;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Min(1)
|
|
||||||
public long getAmount() {
|
|
||||||
return amount;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class CreateBoostResponse {
|
public static class CreateBoostResponse {
|
||||||
|
@ -539,32 +521,24 @@ public class SubscriptionController {
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public CompletableFuture<Response> createBoostPaymentIntent(@NotNull @Valid CreateBoostRequest request) {
|
public CompletableFuture<Response> createBoostPaymentIntent(@NotNull @Valid CreateBoostRequest request) {
|
||||||
return stripeManager.createPaymentIntent(request.getCurrency(), request.getAmount())
|
return CompletableFuture.runAsync(() -> {
|
||||||
|
if (request.level == null) {
|
||||||
|
request.level = boostConfiguration.getLevel();
|
||||||
|
}
|
||||||
|
if (request.level == giftConfiguration.level()) {
|
||||||
|
BigDecimal amountConfigured = giftConfiguration.currencies().get(request.currency.toLowerCase(Locale.ROOT));
|
||||||
|
if (amountConfigured == null || !amountConfigured.equals(BigDecimal.valueOf(request.amount))) {
|
||||||
|
throw new WebApplicationException(Response.status(Status.CONFLICT).entity(Map.of("error", "level_amount_mismatch")).build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.thenCompose(unused -> stripeManager.createPaymentIntent(request.currency, request.amount, request.level))
|
||||||
.thenApply(paymentIntent -> Response.ok(new CreateBoostResponse(paymentIntent.getClientSecret())).build());
|
.thenApply(paymentIntent -> Response.ok(new CreateBoostResponse(paymentIntent.getClientSecret())).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class CreateBoostReceiptCredentialsRequest {
|
public static class CreateBoostReceiptCredentialsRequest {
|
||||||
|
@NotNull public String paymentIntentId;
|
||||||
private final String paymentIntentId;
|
@NotNull public byte[] receiptCredentialRequest;
|
||||||
private final byte[] receiptCredentialRequest;
|
|
||||||
|
|
||||||
@JsonCreator
|
|
||||||
public CreateBoostReceiptCredentialsRequest(
|
|
||||||
@JsonProperty("paymentIntentId") String paymentIntentId,
|
|
||||||
@JsonProperty("receiptCredentialRequest") byte[] receiptCredentialRequest) {
|
|
||||||
this.paymentIntentId = paymentIntentId;
|
|
||||||
this.receiptCredentialRequest = receiptCredentialRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public String getPaymentIntentId() {
|
|
||||||
return paymentIntentId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public byte[] getReceiptCredentialRequest() {
|
|
||||||
return receiptCredentialRequest;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class CreateBoostReceiptCredentialsResponse {
|
public static class CreateBoostReceiptCredentialsResponse {
|
||||||
|
@ -588,7 +562,7 @@ public class SubscriptionController {
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public CompletableFuture<Response> createBoostReceiptCredentials(@NotNull @Valid CreateBoostReceiptCredentialsRequest request) {
|
public CompletableFuture<Response> createBoostReceiptCredentials(@NotNull @Valid CreateBoostReceiptCredentialsRequest request) {
|
||||||
return stripeManager.getPaymentIntent(request.getPaymentIntentId())
|
return stripeManager.getPaymentIntent(request.paymentIntentId)
|
||||||
.thenCompose(paymentIntent -> {
|
.thenCompose(paymentIntent -> {
|
||||||
if (paymentIntent == null) {
|
if (paymentIntent == null) {
|
||||||
throw new WebApplicationException(Status.NOT_FOUND);
|
throw new WebApplicationException(Status.NOT_FOUND);
|
||||||
|
@ -599,22 +573,42 @@ public class SubscriptionController {
|
||||||
if (!StringUtils.equalsIgnoreCase("succeeded", paymentIntent.getStatus())) {
|
if (!StringUtils.equalsIgnoreCase("succeeded", paymentIntent.getStatus())) {
|
||||||
throw new WebApplicationException(Status.PAYMENT_REQUIRED);
|
throw new WebApplicationException(Status.PAYMENT_REQUIRED);
|
||||||
}
|
}
|
||||||
|
long level = boostConfiguration.getLevel();
|
||||||
|
if (paymentIntent.getMetadata() != null) {
|
||||||
|
String levelMetadata = paymentIntent.getMetadata().getOrDefault("level", Long.toString(boostConfiguration.getLevel()));
|
||||||
|
try {
|
||||||
|
level = Long.parseLong(levelMetadata);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
logger.error("failed to parse level metadata ({}) on payment intent {}", levelMetadata, paymentIntent.getId(), e);
|
||||||
|
throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Duration levelExpiration;
|
||||||
|
if (boostConfiguration.getLevel() == level) {
|
||||||
|
levelExpiration = boostConfiguration.getExpiration();
|
||||||
|
} else if (giftConfiguration.level() == level) {
|
||||||
|
levelExpiration = giftConfiguration.expiration();
|
||||||
|
} else {
|
||||||
|
logger.error("level ({}) returned from payment intent that is unknown to the server", level);
|
||||||
|
throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
ReceiptCredentialRequest receiptCredentialRequest;
|
ReceiptCredentialRequest receiptCredentialRequest;
|
||||||
try {
|
try {
|
||||||
receiptCredentialRequest = new ReceiptCredentialRequest(request.getReceiptCredentialRequest());
|
receiptCredentialRequest = new ReceiptCredentialRequest(request.receiptCredentialRequest);
|
||||||
} catch (InvalidInputException e) {
|
} catch (InvalidInputException e) {
|
||||||
throw new BadRequestException("invalid receipt credential request", e);
|
throw new BadRequestException("invalid receipt credential request", e);
|
||||||
}
|
}
|
||||||
|
final long finalLevel = level;
|
||||||
return issuedReceiptsManager.recordIssuance(paymentIntent.getId(), receiptCredentialRequest, clock.instant())
|
return issuedReceiptsManager.recordIssuance(paymentIntent.getId(), receiptCredentialRequest, clock.instant())
|
||||||
.thenApply(unused -> {
|
.thenApply(unused -> {
|
||||||
Instant expiration = Instant.ofEpochSecond(paymentIntent.getCreated())
|
Instant expiration = Instant.ofEpochSecond(paymentIntent.getCreated())
|
||||||
.plus(boostConfiguration.getExpiration())
|
.plus(levelExpiration)
|
||||||
.truncatedTo(ChronoUnit.DAYS)
|
.truncatedTo(ChronoUnit.DAYS)
|
||||||
.plus(1, ChronoUnit.DAYS);
|
.plus(1, ChronoUnit.DAYS);
|
||||||
ReceiptCredentialResponse receiptCredentialResponse;
|
ReceiptCredentialResponse receiptCredentialResponse;
|
||||||
try {
|
try {
|
||||||
receiptCredentialResponse = zkReceiptOperations.issueReceiptCredential(
|
receiptCredentialResponse = zkReceiptOperations.issueReceiptCredential(
|
||||||
receiptCredentialRequest, expiration.getEpochSecond(), boostConfiguration.getLevel());
|
receiptCredentialRequest, expiration.getEpochSecond(), finalLevel);
|
||||||
} catch (VerificationFailedException e) {
|
} catch (VerificationFailedException e) {
|
||||||
throw new BadRequestException("receipt credential request failed verification", e);
|
throw new BadRequestException("receipt credential request failed verification", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,12 +154,13 @@ public class StripeManager {
|
||||||
/**
|
/**
|
||||||
* Creates a payment intent. May throw a 400 WebApplicationException if the amount is too small.
|
* Creates a payment intent. May throw a 400 WebApplicationException if the amount is too small.
|
||||||
*/
|
*/
|
||||||
public CompletableFuture<PaymentIntent> createPaymentIntent(String currency, long amount) {
|
public CompletableFuture<PaymentIntent> createPaymentIntent(String currency, long amount, long level) {
|
||||||
return CompletableFuture.supplyAsync(() -> {
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
PaymentIntentCreateParams params = PaymentIntentCreateParams.builder()
|
PaymentIntentCreateParams params = PaymentIntentCreateParams.builder()
|
||||||
.setAmount(amount)
|
.setAmount(amount)
|
||||||
.setCurrency(currency.toLowerCase(Locale.ROOT))
|
.setCurrency(currency.toLowerCase(Locale.ROOT))
|
||||||
.setDescription(boostDescription)
|
.setDescription(boostDescription)
|
||||||
|
.putMetadata("level", Long.toString(level))
|
||||||
.build();
|
.build();
|
||||||
try {
|
try {
|
||||||
return PaymentIntent.create(params, commonOptions());
|
return PaymentIntent.create(params, commonOptions());
|
||||||
|
|
Loading…
Reference in New Issue