Add `paymentMethod` and `paymentProcessing` fields to `GET /v1/subscription/{subscriberId}` endpoint
This commit is contained in:
parent
e1aa734c40
commit
207ae6129b
|
@ -394,6 +394,7 @@ public class SubscriptionController {
|
||||||
return switch (paymentMethod) {
|
return switch (paymentMethod) {
|
||||||
case CARD, SEPA_DEBIT -> stripeManager;
|
case CARD, SEPA_DEBIT -> stripeManager;
|
||||||
case PAYPAL -> braintreeManager;
|
case PAYPAL -> braintreeManager;
|
||||||
|
case UNKNOWN -> throw new BadRequestException("Invalid payment method");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -886,7 +887,7 @@ public class SubscriptionController {
|
||||||
|
|
||||||
public record Subscription(long level, Instant billingCycleAnchor, Instant endOfCurrentPeriod, boolean active,
|
public record Subscription(long level, Instant billingCycleAnchor, Instant endOfCurrentPeriod, boolean active,
|
||||||
boolean cancelAtPeriodEnd, String currency, BigDecimal amount, String status,
|
boolean cancelAtPeriodEnd, String currency, BigDecimal amount, String status,
|
||||||
SubscriptionProcessor processor) {
|
SubscriptionProcessor processor, PaymentMethod paymentMethod, boolean paymentProcessing) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -919,7 +920,9 @@ public class SubscriptionController {
|
||||||
subscriptionInformation.price().currency(),
|
subscriptionInformation.price().currency(),
|
||||||
subscriptionInformation.price().amount(),
|
subscriptionInformation.price().amount(),
|
||||||
subscriptionInformation.status().getApiValue(),
|
subscriptionInformation.status().getApiValue(),
|
||||||
manager.getProcessor()),
|
manager.getProcessor(),
|
||||||
|
subscriptionInformation.paymentMethod(),
|
||||||
|
subscriptionInformation.paymentProcessing()),
|
||||||
subscriptionInformation.chargeFailure()
|
subscriptionInformation.chargeFailure()
|
||||||
)).build()));
|
)).build()));
|
||||||
});
|
});
|
||||||
|
|
|
@ -426,12 +426,17 @@ public class BraintreeManager implements SubscriptionProcessorManager {
|
||||||
final Instant anchor = subscription.getFirstBillingDate().toInstant();
|
final Instant anchor = subscription.getFirstBillingDate().toInstant();
|
||||||
final Instant endOfCurrentPeriod = subscription.getBillingPeriodEndDate().toInstant();
|
final Instant endOfCurrentPeriod = subscription.getBillingPeriodEndDate().toInstant();
|
||||||
|
|
||||||
final ChargeFailure chargeFailure = getLatestTransactionForSubscription(subscription).map(transaction -> {
|
boolean paymentProcessing = false;
|
||||||
if (getPaymentStatus(transaction.getStatus()).equals(PaymentStatus.SUCCEEDED)) {
|
ChargeFailure chargeFailure = null;
|
||||||
return null;
|
|
||||||
|
final Optional<Transaction> latestTransaction = getLatestTransactionForSubscription(subscription);
|
||||||
|
|
||||||
|
if (latestTransaction.isPresent()){
|
||||||
|
paymentProcessing = isPaymentProcessing(latestTransaction.get().getStatus());
|
||||||
|
if (!getPaymentStatus(latestTransaction.get().getStatus()).equals(PaymentStatus.SUCCEEDED)) {
|
||||||
|
chargeFailure = createChargeFailure(latestTransaction.get());
|
||||||
}
|
}
|
||||||
return createChargeFailure(transaction);
|
}
|
||||||
}).orElse(null);
|
|
||||||
|
|
||||||
return new SubscriptionInformation(
|
return new SubscriptionInformation(
|
||||||
new SubscriptionPrice(plan.getCurrencyIsoCode().toUpperCase(Locale.ROOT),
|
new SubscriptionPrice(plan.getCurrencyIsoCode().toUpperCase(Locale.ROOT),
|
||||||
|
@ -442,11 +447,25 @@ public class BraintreeManager implements SubscriptionProcessorManager {
|
||||||
Subscription.Status.ACTIVE == subscription.getStatus(),
|
Subscription.Status.ACTIVE == subscription.getStatus(),
|
||||||
!subscription.neverExpires(),
|
!subscription.neverExpires(),
|
||||||
getSubscriptionStatus(subscription.getStatus()),
|
getSubscriptionStatus(subscription.getStatus()),
|
||||||
|
latestTransaction.map(this::getPaymentMethodFromTransaction).orElse(PaymentMethod.PAYPAL),
|
||||||
|
paymentProcessing,
|
||||||
chargeFailure
|
chargeFailure
|
||||||
);
|
);
|
||||||
}, executor);
|
}, executor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PaymentMethod getPaymentMethodFromTransaction(Transaction transaction) {
|
||||||
|
if (transaction.getPayPalDetails() != null) {
|
||||||
|
return PaymentMethod.PAYPAL;
|
||||||
|
}
|
||||||
|
logger.error("Unexpected payment method from Braintree: {}, transaction id {}", transaction.getPaymentInstrumentType(), transaction.getId());
|
||||||
|
return PaymentMethod.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isPaymentProcessing(final Transaction.Status status) {
|
||||||
|
return status == Transaction.Status.SETTLEMENT_PENDING;
|
||||||
|
}
|
||||||
|
|
||||||
private ChargeFailure createChargeFailure(Transaction transaction) {
|
private ChargeFailure createChargeFailure(Transaction transaction) {
|
||||||
|
|
||||||
final String code;
|
final String code;
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
package org.whispersystems.textsecuregcm.subscriptions;
|
package org.whispersystems.textsecuregcm.subscriptions;
|
||||||
|
|
||||||
public enum PaymentMethod {
|
public enum PaymentMethod {
|
||||||
|
UNKNOWN,
|
||||||
/**
|
/**
|
||||||
* A credit card or debit card, including those from Apple Pay and Google Pay
|
* A credit card or debit card, including those from Apple Pay and Google Pay
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -67,10 +67,12 @@ import javax.ws.rs.WebApplicationException;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.textsecuregcm.util.Conversions;
|
import org.whispersystems.textsecuregcm.util.Conversions;
|
||||||
|
|
||||||
public class StripeManager implements SubscriptionProcessorManager {
|
public class StripeManager implements SubscriptionProcessorManager {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(StripeManager.class);
|
||||||
private static final String METADATA_KEY_LEVEL = "level";
|
private static final String METADATA_KEY_LEVEL = "level";
|
||||||
|
|
||||||
private final StripeClient stripeClient;
|
private final StripeClient stripeClient;
|
||||||
|
@ -483,17 +485,30 @@ public class StripeManager implements SubscriptionProcessorManager {
|
||||||
return getPriceForSubscription(subscription).thenCompose(price ->
|
return getPriceForSubscription(subscription).thenCompose(price ->
|
||||||
getLevelForPrice(price).thenApply(level -> {
|
getLevelForPrice(price).thenApply(level -> {
|
||||||
ChargeFailure chargeFailure = null;
|
ChargeFailure chargeFailure = null;
|
||||||
|
boolean paymentProcessing = false;
|
||||||
|
PaymentMethod paymentMethod = null;
|
||||||
|
|
||||||
if (subscription.getLatestInvoiceObject() != null && subscription.getLatestInvoiceObject().getChargeObject() != null &&
|
if (subscription.getLatestInvoiceObject() != null) {
|
||||||
(subscription.getLatestInvoiceObject().getChargeObject().getFailureCode() != null || subscription.getLatestInvoiceObject().getChargeObject().getFailureMessage() != null)) {
|
final Invoice invoice = subscription.getLatestInvoiceObject();
|
||||||
Charge charge = subscription.getLatestInvoiceObject().getChargeObject();
|
paymentProcessing = "open".equals(invoice.getStatus());
|
||||||
Charge.Outcome outcome = charge.getOutcome();
|
|
||||||
chargeFailure = new ChargeFailure(
|
if (invoice.getChargeObject() != null) {
|
||||||
|
final Charge charge = invoice.getChargeObject();
|
||||||
|
if (charge.getFailureCode() != null || charge.getFailureMessage() != null) {
|
||||||
|
Charge.Outcome outcome = charge.getOutcome();
|
||||||
|
chargeFailure = new ChargeFailure(
|
||||||
charge.getFailureCode(),
|
charge.getFailureCode(),
|
||||||
charge.getFailureMessage(),
|
charge.getFailureMessage(),
|
||||||
outcome != null ? outcome.getNetworkStatus() : null,
|
outcome != null ? outcome.getNetworkStatus() : null,
|
||||||
outcome != null ? outcome.getReason() : null,
|
outcome != null ? outcome.getReason() : null,
|
||||||
outcome != null ? outcome.getType() : null);
|
outcome != null ? outcome.getType() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (charge.getPaymentMethodDetails() != null
|
||||||
|
&& charge.getPaymentMethodDetails().getType() != null) {
|
||||||
|
paymentMethod = getPaymentMethodFromStripeString(charge.getPaymentMethodDetails().getType(), invoice.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SubscriptionInformation(
|
return new SubscriptionInformation(
|
||||||
|
@ -504,11 +519,24 @@ public class StripeManager implements SubscriptionProcessorManager {
|
||||||
Objects.equals(subscription.getStatus(), "active"),
|
Objects.equals(subscription.getStatus(), "active"),
|
||||||
subscription.getCancelAtPeriodEnd(),
|
subscription.getCancelAtPeriodEnd(),
|
||||||
getSubscriptionStatus(subscription.getStatus()),
|
getSubscriptionStatus(subscription.getStatus()),
|
||||||
|
paymentMethod,
|
||||||
|
paymentProcessing,
|
||||||
chargeFailure
|
chargeFailure
|
||||||
);
|
);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static PaymentMethod getPaymentMethodFromStripeString(final String paymentMethodString, final String invoiceId) {
|
||||||
|
return switch (paymentMethodString) {
|
||||||
|
case "sepa_debit" -> PaymentMethod.SEPA_DEBIT;
|
||||||
|
case "card" -> PaymentMethod.CARD;
|
||||||
|
default -> {
|
||||||
|
logger.error("Unexpected payment method from Stripe: {}, invoice id: {}", paymentMethodString, invoiceId);
|
||||||
|
yield PaymentMethod.UNKNOWN;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private Subscription getSubscription(Object subscriptionObj) {
|
private Subscription getSubscription(Object subscriptionObj) {
|
||||||
if (!(subscriptionObj instanceof final Subscription subscription)) {
|
if (!(subscriptionObj instanceof final Subscription subscription)) {
|
||||||
throw new IllegalArgumentException("invalid subscription object: " + subscriptionObj.getClass().getName());
|
throw new IllegalArgumentException("invalid subscription object: " + subscriptionObj.getClass().getName());
|
||||||
|
|
|
@ -144,8 +144,8 @@ public interface SubscriptionProcessorManager {
|
||||||
|
|
||||||
record SubscriptionInformation(SubscriptionPrice price, long level, Instant billingCycleAnchor,
|
record SubscriptionInformation(SubscriptionPrice price, long level, Instant billingCycleAnchor,
|
||||||
Instant endOfCurrentPeriod, boolean active, boolean cancelAtPeriodEnd,
|
Instant endOfCurrentPeriod, boolean active, boolean cancelAtPeriodEnd,
|
||||||
SubscriptionStatus status,
|
SubscriptionStatus status, PaymentMethod paymentMethod, boolean paymentProcessing,
|
||||||
ChargeFailure chargeFailure) {
|
@Nullable ChargeFailure chargeFailure) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue