From 9ee081ef281ac5d26ed29be764db258ccd9d1abc Mon Sep 17 00:00:00 2001 From: Ravi Khadiwala Date: Mon, 31 Aug 2026 10:20:39 -0500 Subject: [PATCH] Add playOptionId to login configuration --- service/config/sample.yml | 1 + .../configuration/LoginPurchaseConfiguration.java | 2 ++ .../textsecuregcm/controllers/SubscriptionController.java | 6 +++++- .../textsecuregcm/grpc/ProductConfigurationGrpcService.java | 1 + .../main/proto/org/signal/chat/product_configuration.proto | 4 +++- .../controllers/SubscriptionControllerTest.java | 3 ++- .../grpc/ProductConfigurationGrpcServiceTest.java | 3 ++- service/src/test/resources/config/test.yml | 1 + 8 files changed, 17 insertions(+), 4 deletions(-) diff --git a/service/config/sample.yml b/service/config/sample.yml index f99705455..7516acbeb 100644 --- a/service/config/sample.yml +++ b/service/config/sample.yml @@ -387,6 +387,7 @@ subscription: # configuration for Stripe subscriptions loginPurchase: playProductId: login_example + playOptionId: login_option_example appStoreProductId: login_example oneTimeDonations: diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/configuration/LoginPurchaseConfiguration.java b/service/src/main/java/org/whispersystems/textsecuregcm/configuration/LoginPurchaseConfiguration.java index 08cfa9d47..2646e6bfa 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/configuration/LoginPurchaseConfiguration.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/configuration/LoginPurchaseConfiguration.java @@ -12,7 +12,9 @@ import java.time.Duration; /// Configuration for one-time Signal Login purchases /// /// @param playProductId the Google Play Billing productId clients should purchase to obtain a Signal Login +/// @param playOptionId the Google Play Billing optionId clients should purchase to obtain a Signal Login /// @param appStoreProductId the App Store productId clients should purchase to obtain a Signal Login public record LoginPurchaseConfiguration( @NotEmpty String playProductId, + @NotEmpty String playOptionId, @NotEmpty String appStoreProductId) {} 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 cbb545a1f..8544d3018 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/SubscriptionController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/SubscriptionController.java @@ -171,7 +171,9 @@ public class SubscriptionController { buildDonationLevelsConfiguration(subscriptionConfiguration, oneTimeDonationConfiguration, badgeTranslator, acceptableLanguages), new BackupConfiguration(backupLevels, subscriptionConfiguration.getbackupFreeTierMediaDuration().toDays()), - new LoginConfiguration(ReceiptLevel.LOGIN.getValue(), loginPurchaseConfiguration.playProductId(), + new LoginConfiguration(ReceiptLevel.LOGIN.getValue(), + loginPurchaseConfiguration.playProductId(), + loginPurchaseConfiguration.playOptionId(), loginPurchaseConfiguration.appStoreProductId()), oneTimeDonationConfiguration.sepaMaximumEuros()); } @@ -549,6 +551,8 @@ public class SubscriptionController { long level, @Schema(description = "The play billing productID associated with a Signal Login purchase") String playProductId, + @Schema(description = "The play billing optionID to use to purchase a Signal Login") + String playOptionId, @Schema(description = "The App Store productID associated with a Signal Login purchase") String appStoreProductId) {} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/grpc/ProductConfigurationGrpcService.java b/service/src/main/java/org/whispersystems/textsecuregcm/grpc/ProductConfigurationGrpcService.java index 8c87296f9..ab22a4f7f 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/grpc/ProductConfigurationGrpcService.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/grpc/ProductConfigurationGrpcService.java @@ -95,6 +95,7 @@ public class ProductConfigurationGrpcService extends SimpleProductConfigurationG return LoginConfiguration.newBuilder() .setLevel(ReceiptLevel.LOGIN.getValue()) .setPlayProductId(loginPurchaseConfiguration.playProductId()) + .setPlayOptionId(loginPurchaseConfiguration.playOptionId()) .setAppStoreProductId(loginPurchaseConfiguration.appStoreProductId()) .build(); } diff --git a/service/src/main/proto/org/signal/chat/product_configuration.proto b/service/src/main/proto/org/signal/chat/product_configuration.proto index c9e894e66..c8f9fdc3e 100644 --- a/service/src/main/proto/org/signal/chat/product_configuration.proto +++ b/service/src/main/proto/org/signal/chat/product_configuration.proto @@ -90,6 +90,8 @@ message LoginConfiguration { uint64 level = 1; // The play billing productID associated with a Signal Login purchase string play_product_id = 2; + // The play billing optionID to use to purchase a Signal Login + string play_option_id = 5; // The App Store productID associated with a Signal Login purchase - string app_store_product_id = 3; + string app_store_product_id = 4; } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/SubscriptionControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/SubscriptionControllerTest.java index 61529dff4..a619b0d25 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/SubscriptionControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/SubscriptionControllerTest.java @@ -112,7 +112,7 @@ class SubscriptionControllerTest extends AbstractV1SubscriptionControllerTest { private static final BadgeTranslator BADGE_TRANSLATOR = mock(BadgeTranslator.class); private static final BankMandateTranslator BANK_MANDATE_TRANSLATOR = mock(BankMandateTranslator.class); private static final LoginPurchaseConfiguration LOGIN_PURCHASE_CONFIG = - new LoginPurchaseConfiguration("testLoginPlayProductId", "testLoginAppStoreProductId"); + new LoginPurchaseConfiguration("testLoginPlayProductId", "testLoginPlayOptionId", "testLoginAppStoreProductId"); private final static SubscriptionController SUBSCRIPTION_CONTROLLER = new SubscriptionController(CLOCK, SUBSCRIPTION_CONFIG, ONETIME_CONFIG, LOGIN_PURCHASE_CONFIG, new SubscriptionManager(SUBSCRIPTIONS, List.of(STRIPE_MANAGER, BRAINTREE_MANAGER, PLAY_MANAGER, APPSTORE_MANAGER), @@ -1080,6 +1080,7 @@ class SubscriptionControllerTest extends AbstractV1SubscriptionControllerTest { assertThat(response.login().level()).isEqualTo(ReceiptLevel.LOGIN.getValue()); assertThat(response.login().playProductId()).isEqualTo(LOGIN_PURCHASE_CONFIG.playProductId()); + assertThat(response.login().playOptionId()).isEqualTo(LOGIN_PURCHASE_CONFIG.playOptionId()); assertThat(response.login().appStoreProductId()).isEqualTo(LOGIN_PURCHASE_CONFIG.appStoreProductId()); // check the badge vs purchasable badge fields diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/grpc/ProductConfigurationGrpcServiceTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/grpc/ProductConfigurationGrpcServiceTest.java index 09d7cf85e..8bdc124cf 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/grpc/ProductConfigurationGrpcServiceTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/grpc/ProductConfigurationGrpcServiceTest.java @@ -40,7 +40,7 @@ public class ProductConfigurationGrpcServiceTest extends SubscriptionConfigTestHelper.getOneTimeConfig(); private static final LoginPurchaseConfiguration LOGIN_PURCHASE_CONFIGURATION = - new LoginPurchaseConfiguration("testLoginPlayProductId", "testLoginAppStoreProductId"); + new LoginPurchaseConfiguration("testLoginPlayProductId", "testLoginPlayOptionId", "testLoginAppStoreProductId"); @Mock private StripeManager stripeManager; @@ -135,6 +135,7 @@ public class ProductConfigurationGrpcServiceTest extends assertEquals(ReceiptLevel.LOGIN.getValue(), configuration.getLogin().getLevel()); assertEquals(LOGIN_PURCHASE_CONFIGURATION.playProductId(), configuration.getLogin().getPlayProductId()); + assertEquals(LOGIN_PURCHASE_CONFIGURATION.playOptionId(), configuration.getLogin().getPlayOptionId()); assertEquals(LOGIN_PURCHASE_CONFIGURATION.appStoreProductId(), configuration.getLogin().getAppStoreProductId()); } diff --git a/service/src/test/resources/config/test.yml b/service/src/test/resources/config/test.yml index 4e573c44f..a7182c39a 100644 --- a/service/src/test/resources/config/test.yml +++ b/service/src/test/resources/config/test.yml @@ -399,6 +399,7 @@ subscription: # configuration for Stripe subscriptions loginPurchase: playProductId: login_example + playOptionId: login_option_example appStoreProductId: login_example oneTimeDonations: