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) { | ||||
|       case CARD, SEPA_DEBIT -> stripeManager; | ||||
|       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, | ||||
|                                  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().amount(), | ||||
|                             subscriptionInformation.status().getApiValue(), | ||||
|                             manager.getProcessor()), | ||||
|                             manager.getProcessor(), | ||||
|                             subscriptionInformation.paymentMethod(), | ||||
|                             subscriptionInformation.paymentProcessing()), | ||||
|                         subscriptionInformation.chargeFailure() | ||||
|                     )).build())); | ||||
|         }); | ||||
|  |  | |||
|  | @ -426,12 +426,17 @@ public class BraintreeManager implements SubscriptionProcessorManager { | |||
|       final Instant anchor = subscription.getFirstBillingDate().toInstant(); | ||||
|       final Instant endOfCurrentPeriod = subscription.getBillingPeriodEndDate().toInstant(); | ||||
| 
 | ||||
|       final ChargeFailure chargeFailure = getLatestTransactionForSubscription(subscription).map(transaction -> { | ||||
|         if (getPaymentStatus(transaction.getStatus()).equals(PaymentStatus.SUCCEEDED)) { | ||||
|           return null; | ||||
|       boolean paymentProcessing = false; | ||||
|       ChargeFailure chargeFailure = 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( | ||||
|           new SubscriptionPrice(plan.getCurrencyIsoCode().toUpperCase(Locale.ROOT), | ||||
|  | @ -442,11 +447,25 @@ public class BraintreeManager implements SubscriptionProcessorManager { | |||
|           Subscription.Status.ACTIVE == subscription.getStatus(), | ||||
|           !subscription.neverExpires(), | ||||
|           getSubscriptionStatus(subscription.getStatus()), | ||||
|           latestTransaction.map(this::getPaymentMethodFromTransaction).orElse(PaymentMethod.PAYPAL), | ||||
|           paymentProcessing, | ||||
|           chargeFailure | ||||
|       ); | ||||
|     }, 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) { | ||||
| 
 | ||||
|     final String code; | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ | |||
| package org.whispersystems.textsecuregcm.subscriptions; | ||||
| 
 | ||||
| public enum PaymentMethod { | ||||
|   UNKNOWN, | ||||
|   /** | ||||
|    * 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.Status; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.whispersystems.textsecuregcm.util.Conversions; | ||||
| 
 | ||||
| public class StripeManager implements SubscriptionProcessorManager { | ||||
| 
 | ||||
|   private static final Logger logger = LoggerFactory.getLogger(StripeManager.class); | ||||
|   private static final String METADATA_KEY_LEVEL = "level"; | ||||
| 
 | ||||
|   private final StripeClient stripeClient; | ||||
|  | @ -483,10 +485,16 @@ public class StripeManager implements SubscriptionProcessorManager { | |||
|     return getPriceForSubscription(subscription).thenCompose(price -> | ||||
|             getLevelForPrice(price).thenApply(level -> { | ||||
|               ChargeFailure chargeFailure = null; | ||||
|               boolean paymentProcessing = false; | ||||
|               PaymentMethod paymentMethod = null; | ||||
| 
 | ||||
|               if (subscription.getLatestInvoiceObject() != null && subscription.getLatestInvoiceObject().getChargeObject() != null && | ||||
|                       (subscription.getLatestInvoiceObject().getChargeObject().getFailureCode() != null || subscription.getLatestInvoiceObject().getChargeObject().getFailureMessage() != null)) { | ||||
|                 Charge charge = subscription.getLatestInvoiceObject().getChargeObject(); | ||||
|               if (subscription.getLatestInvoiceObject() != null) { | ||||
|                 final Invoice invoice = subscription.getLatestInvoiceObject(); | ||||
|                 paymentProcessing = "open".equals(invoice.getStatus()); | ||||
| 
 | ||||
|                 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(), | ||||
|  | @ -496,6 +504,13 @@ public class StripeManager implements SubscriptionProcessorManager { | |||
|                         outcome != null ? outcome.getType() : null); | ||||
|                   } | ||||
| 
 | ||||
|                   if (charge.getPaymentMethodDetails() != null | ||||
|                       && charge.getPaymentMethodDetails().getType() != null) { | ||||
|                     paymentMethod = getPaymentMethodFromStripeString(charge.getPaymentMethodDetails().getType(), invoice.getId()); | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
| 
 | ||||
|               return new SubscriptionInformation( | ||||
|                   new SubscriptionPrice(price.getCurrency().toUpperCase(Locale.ROOT), price.getUnitAmountDecimal()), | ||||
|                   level, | ||||
|  | @ -504,11 +519,24 @@ public class StripeManager implements SubscriptionProcessorManager { | |||
|                   Objects.equals(subscription.getStatus(), "active"), | ||||
|                   subscription.getCancelAtPeriodEnd(), | ||||
|                   getSubscriptionStatus(subscription.getStatus()), | ||||
|                   paymentMethod, | ||||
|                   paymentProcessing, | ||||
|                   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) { | ||||
|     if (!(subscriptionObj instanceof final Subscription subscription)) { | ||||
|       throw new IllegalArgumentException("invalid subscription object: " + subscriptionObj.getClass().getName()); | ||||
|  |  | |||
|  | @ -144,8 +144,8 @@ public interface SubscriptionProcessorManager { | |||
| 
 | ||||
|   record SubscriptionInformation(SubscriptionPrice price, long level, Instant billingCycleAnchor, | ||||
|                                  Instant endOfCurrentPeriod, boolean active, boolean cancelAtPeriodEnd, | ||||
|                                  SubscriptionStatus status, | ||||
|                                  ChargeFailure chargeFailure) { | ||||
|                                  SubscriptionStatus status, PaymentMethod paymentMethod, boolean paymentProcessing, | ||||
|                                  @Nullable ChargeFailure chargeFailure) { | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Katherine
						Katherine