From 9fb4e2d272efd49e0b6a7075862c4840b0fbd7cc Mon Sep 17 00:00:00 2001 From: Ravi Khadiwala Date: Thu, 5 Sep 2024 18:07:56 -0500 Subject: [PATCH] set billingCycleAnchor in play billing responses --- .../controllers/SubscriptionController.java | 2 +- .../GooglePlayBillingManager.java | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/SubscriptionController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/SubscriptionController.java index 0e0255c49..526b8656d 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/SubscriptionController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/SubscriptionController.java @@ -527,7 +527,7 @@ public class SubscriptionController { long level, @Schema( - description = "If present, UNIX Epoch Timestamp in seconds, can be used to calculate next billing date. May be absent for IAP subscriptions", + description = "If present, UNIX Epoch Timestamp in seconds, can be used to calculate next billing date.", externalDocs = @ExternalDocumentation(description = "Calculate next billing date", url = "https://stripe.com/docs/billing/subscriptions/billing-cycle")) Instant billingCycleAnchor, diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/GooglePlayBillingManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/GooglePlayBillingManager.java index 23eddb558..61db28d19 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/GooglePlayBillingManager.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/GooglePlayBillingManager.java @@ -230,6 +230,7 @@ public class GooglePlayBillingManager implements SubscriptionPaymentProcessor { return subscriptionFuture.thenCombineAsync(priceFuture, (subscription, price) -> { final SubscriptionPurchaseLineItem lineItem = getLineItem(subscription); + final Optional billingCycleAnchor = getStartTime(subscription); final Optional expiration = getExpiration(lineItem); final SubscriptionStatus status = switch (SubscriptionState @@ -246,7 +247,8 @@ public class GooglePlayBillingManager implements SubscriptionPaymentProcessor { return new SubscriptionInformation( price, productIdToLevel(lineItem.getProductId()), - null, expiration.orElse(null), + billingCycleAnchor.orElse(null), + expiration.orElse(null), expiration.map(clock.instant()::isBefore).orElse(false), lineItem.getAutoRenewingPlan() != null && lineItem.getAutoRenewingPlan().getAutoRenewEnabled(), status, @@ -385,18 +387,27 @@ public class GooglePlayBillingManager implements SubscriptionPaymentProcessor { "acknowledgementState", subscription.getAcknowledgementState()); } + private Optional getStartTime(final SubscriptionPurchaseV2 subscription) { + return parseTimestamp(subscription.getStartTime()); + } + private Optional getExpiration(final SubscriptionPurchaseLineItem purchaseLineItem) { - if (StringUtils.isBlank(purchaseLineItem.getExpiryTime())) { + return parseTimestamp(purchaseLineItem.getExpiryTime()); + } + + private Optional parseTimestamp(final String timestamp) { + if (StringUtils.isBlank(timestamp)) { return Optional.empty(); } try { - return Optional.of(Instant.parse(purchaseLineItem.getExpiryTime())); + return Optional.of(Instant.parse(timestamp)); } catch (DateTimeParseException e) { - logger.warn("received an expiry time with an invalid format: {}", purchaseLineItem.getExpiryTime()); + logger.warn("received a timestamp with an invalid format: {}", timestamp); return Optional.empty(); } } + // https://developers.google.com/android-publisher/api-ref/rest/v3/purchases.subscriptionsv2#SubscriptionState @VisibleForTesting enum SubscriptionState {