Use latest invoice on subscription to generate receipts
This commit is contained in:
parent
6547d5ebf3
commit
279b0a51d9
|
@ -21,7 +21,6 @@ import java.time.Instant;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -743,7 +742,7 @@ public class SubscriptionController {
|
||||||
.thenApply(this::requireRecordFromGetResult)
|
.thenApply(this::requireRecordFromGetResult)
|
||||||
.thenCompose(record -> {
|
.thenCompose(record -> {
|
||||||
if (record.subscriptionId == null) {
|
if (record.subscriptionId == null) {
|
||||||
return CompletableFuture.completedFuture(Response.noContent().build());
|
return CompletableFuture.completedFuture(Response.status(Status.NOT_FOUND).build());
|
||||||
}
|
}
|
||||||
ReceiptCredentialRequest receiptCredentialRequest;
|
ReceiptCredentialRequest receiptCredentialRequest;
|
||||||
try {
|
try {
|
||||||
|
@ -751,29 +750,20 @@ public class SubscriptionController {
|
||||||
} catch (InvalidInputException e) {
|
} catch (InvalidInputException e) {
|
||||||
throw new BadRequestException("invalid receipt credential request", e);
|
throw new BadRequestException("invalid receipt credential request", e);
|
||||||
}
|
}
|
||||||
return stripeManager.getPaidInvoicesForSubscription(record.subscriptionId, requestData.now)
|
return stripeManager.getLatestInvoiceForSubscription(record.subscriptionId)
|
||||||
.thenCompose(invoices -> checkNextInvoice(invoices.iterator(), record.subscriptionId))
|
.thenCompose(invoice -> convertInvoiceToReceipt(invoice, record.subscriptionId))
|
||||||
.thenCompose(receipt -> {
|
.thenCompose(receipt -> issuedReceiptsManager.recordIssuance(
|
||||||
if (receipt == null) {
|
receipt.getInvoiceLineItemId(), receiptCredentialRequest, requestData.now)
|
||||||
return CompletableFuture.completedFuture(null);
|
.thenApply(unused -> receipt))
|
||||||
}
|
|
||||||
return issuedReceiptsManager.recordIssuance(
|
|
||||||
receipt.getInvoiceLineItemId(), receiptCredentialRequest, requestData.now)
|
|
||||||
.thenApply(unused -> receipt);
|
|
||||||
})
|
|
||||||
.thenApply(receipt -> {
|
.thenApply(receipt -> {
|
||||||
if (receipt == null) {
|
ReceiptCredentialResponse receiptCredentialResponse;
|
||||||
return Response.noContent().build();
|
try {
|
||||||
} else {
|
receiptCredentialResponse = zkReceiptOperations.issueReceiptCredential(
|
||||||
ReceiptCredentialResponse receiptCredentialResponse;
|
receiptCredentialRequest, receipt.getExpiration().getEpochSecond(), receipt.getLevel());
|
||||||
try {
|
} catch (VerificationFailedException e) {
|
||||||
receiptCredentialResponse = zkReceiptOperations.issueReceiptCredential(
|
throw new BadRequestException("receipt credential request failed verification", e);
|
||||||
receiptCredentialRequest, receipt.getExpiration().getEpochSecond(), receipt.getLevel());
|
|
||||||
} catch (VerificationFailedException e) {
|
|
||||||
throw new BadRequestException("receipt credential request failed verification", e);
|
|
||||||
}
|
|
||||||
return Response.ok(new GetReceiptCredentialsResponse(receiptCredentialResponse.serialize())).build();
|
|
||||||
}
|
}
|
||||||
|
return Response.ok(new GetReceiptCredentialsResponse(receiptCredentialResponse.serialize())).build();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -803,35 +793,46 @@ public class SubscriptionController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private CompletableFuture<Receipt> checkNextInvoice(Iterator<Invoice> invoiceIterator, String subscriptionId) {
|
private CompletableFuture<Receipt> convertInvoiceToReceipt(Invoice latestSubscriptionInvoice, String subscriptionId) {
|
||||||
if (!invoiceIterator.hasNext()) {
|
if (latestSubscriptionInvoice == null) {
|
||||||
return null;
|
throw new WebApplicationException(Status.NO_CONTENT);
|
||||||
|
}
|
||||||
|
if (StringUtils.equalsIgnoreCase("open", latestSubscriptionInvoice.getStatus())) {
|
||||||
|
throw new WebApplicationException(Status.NO_CONTENT);
|
||||||
|
}
|
||||||
|
if (!StringUtils.equalsIgnoreCase("paid", latestSubscriptionInvoice.getStatus())) {
|
||||||
|
throw new WebApplicationException(Status.PAYMENT_REQUIRED);
|
||||||
}
|
}
|
||||||
|
|
||||||
Invoice invoice = invoiceIterator.next();
|
return stripeManager.getInvoiceLineItemsForInvoice(latestSubscriptionInvoice).thenCompose(invoiceLineItems -> {
|
||||||
return stripeManager.getInvoiceLineItemsForInvoice(invoice).thenCompose(invoiceLineItems -> {
|
|
||||||
Collection<InvoiceLineItem> subscriptionLineItems = invoiceLineItems.stream()
|
Collection<InvoiceLineItem> subscriptionLineItems = invoiceLineItems.stream()
|
||||||
.filter(invoiceLineItem -> Objects.equals("subscription", invoiceLineItem.getType()))
|
.filter(invoiceLineItem -> Objects.equals("subscription", invoiceLineItem.getType()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
if (subscriptionLineItems.isEmpty()) {
|
if (subscriptionLineItems.isEmpty()) {
|
||||||
return checkNextInvoice(invoiceIterator, subscriptionId);
|
throw new IllegalStateException("latest subscription invoice has no subscription line items; subscriptionId="
|
||||||
|
+ subscriptionId + "; invoiceId=" + latestSubscriptionInvoice.getId());
|
||||||
}
|
}
|
||||||
if (subscriptionLineItems.size() > 1) {
|
if (subscriptionLineItems.size() > 1) {
|
||||||
throw new IllegalStateException("invoice has more than one subscription; subscriptionId=" + subscriptionId
|
throw new IllegalStateException(
|
||||||
+ "; count=" + subscriptionLineItems.size());
|
"latest subscription invoice has too many subscription line items; subscriptionId=" + subscriptionId
|
||||||
|
+ "; invoiceId=" + latestSubscriptionInvoice.getId() + "; count=" + subscriptionLineItems.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
InvoiceLineItem subscriptionLineItem = subscriptionLineItems.stream().findAny().get();
|
InvoiceLineItem subscriptionLineItem = subscriptionLineItems.stream().findAny().get();
|
||||||
return stripeManager.getProductForPrice(subscriptionLineItem.getPrice().getId()).thenApply(product -> new Receipt(
|
return getReceiptForSubscriptionInvoiceLineItem(subscriptionLineItem);
|
||||||
Instant.ofEpochSecond(subscriptionLineItem.getPeriod().getEnd())
|
|
||||||
.plus(subscriptionConfiguration.getBadgeGracePeriod())
|
|
||||||
.truncatedTo(ChronoUnit.DAYS)
|
|
||||||
.plus(1, ChronoUnit.DAYS),
|
|
||||||
stripeManager.getLevelForProduct(product),
|
|
||||||
subscriptionLineItem.getId()));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CompletableFuture<Receipt> getReceiptForSubscriptionInvoiceLineItem(InvoiceLineItem subscriptionLineItem) {
|
||||||
|
return stripeManager.getProductForPrice(subscriptionLineItem.getPrice().getId()).thenApply(product -> new Receipt(
|
||||||
|
Instant.ofEpochSecond(subscriptionLineItem.getPeriod().getEnd())
|
||||||
|
.plus(subscriptionConfiguration.getBadgeGracePeriod())
|
||||||
|
.truncatedTo(ChronoUnit.DAYS)
|
||||||
|
.plus(1, ChronoUnit.DAYS),
|
||||||
|
stripeManager.getLevelForProduct(product),
|
||||||
|
subscriptionLineItem.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
private SubscriptionManager.Record requireRecordFromGetResult(SubscriptionManager.GetResult getResult) {
|
private SubscriptionManager.Record requireRecordFromGetResult(SubscriptionManager.GetResult getResult) {
|
||||||
if (getResult == GetResult.PASSWORD_MISMATCH) {
|
if (getResult == GetResult.PASSWORD_MISMATCH) {
|
||||||
throw new ForbiddenException("subscriberId mismatch");
|
throw new ForbiddenException("subscriberId mismatch");
|
||||||
|
|
|
@ -29,6 +29,7 @@ import com.stripe.param.SetupIntentCreateParams;
|
||||||
import com.stripe.param.SubscriptionCancelParams;
|
import com.stripe.param.SubscriptionCancelParams;
|
||||||
import com.stripe.param.SubscriptionCreateParams;
|
import com.stripe.param.SubscriptionCreateParams;
|
||||||
import com.stripe.param.SubscriptionListParams;
|
import com.stripe.param.SubscriptionListParams;
|
||||||
|
import com.stripe.param.SubscriptionRetrieveParams;
|
||||||
import com.stripe.param.SubscriptionUpdateParams;
|
import com.stripe.param.SubscriptionUpdateParams;
|
||||||
import com.stripe.param.SubscriptionUpdateParams.BillingCycleAnchor;
|
import com.stripe.param.SubscriptionUpdateParams.BillingCycleAnchor;
|
||||||
import com.stripe.param.SubscriptionUpdateParams.ProrationBehavior;
|
import com.stripe.param.SubscriptionUpdateParams.ProrationBehavior;
|
||||||
|
@ -345,6 +346,19 @@ public class StripeManager {
|
||||||
}, executor);
|
}, executor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CompletableFuture<Invoice> getLatestInvoiceForSubscription(String subscriptionId) {
|
||||||
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
|
SubscriptionRetrieveParams params = SubscriptionRetrieveParams.builder()
|
||||||
|
.addExpand("latest_invoice")
|
||||||
|
.build();
|
||||||
|
try {
|
||||||
|
return Subscription.retrieve(subscriptionId, params, commonOptions()).getLatestInvoiceObject();
|
||||||
|
} catch (StripeException e) {
|
||||||
|
throw new CompletionException(e);
|
||||||
|
}
|
||||||
|
}, executor);
|
||||||
|
}
|
||||||
|
|
||||||
public CompletableFuture<Collection<InvoiceLineItem>> getInvoiceLineItemsForInvoice(Invoice invoice) {
|
public CompletableFuture<Collection<InvoiceLineItem>> getInvoiceLineItemsForInvoice(Invoice invoice) {
|
||||||
return CompletableFuture.supplyAsync(
|
return CompletableFuture.supplyAsync(
|
||||||
() -> Lists.newArrayList(invoice.getLines().autoPagingIterable(null, commonOptions())), executor);
|
() -> Lists.newArrayList(invoice.getLines().autoPagingIterable(null, commonOptions())), executor);
|
||||||
|
|
Loading…
Reference in New Issue