Skip to content

Commit

Permalink
Define endpoint to get localized bank mandate text
Browse files Browse the repository at this point in the history
  • Loading branch information
katherine-signal authored Oct 5, 2023
1 parent 9b1b03b commit e1aa734
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@
import org.whispersystems.textsecuregcm.storage.SubscriptionManager;
import org.whispersystems.textsecuregcm.storage.VerificationSessionManager;
import org.whispersystems.textsecuregcm.storage.VerificationSessions;
import org.whispersystems.textsecuregcm.subscriptions.BankMandateTranslator;
import org.whispersystems.textsecuregcm.subscriptions.BraintreeManager;
import org.whispersystems.textsecuregcm.subscriptions.StripeManager;
import org.whispersystems.textsecuregcm.util.DynamoDbFromConfig;
Expand Down Expand Up @@ -294,6 +295,7 @@ public void run(WhisperServerConfiguration config, Environment environment) thro
clock, config.getBadges(), headerControlledResourceBundleLookup);
ResourceBundleLevelTranslator resourceBundleLevelTranslator = new ResourceBundleLevelTranslator(
headerControlledResourceBundleLookup);
BankMandateTranslator bankMandateTranslator = new BankMandateTranslator(headerControlledResourceBundleLookup);

DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbFromConfig.asyncClient(config.getDynamoDbClientConfiguration(),
AWSSDK_CREDENTIALS_PROVIDER);
Expand Down Expand Up @@ -804,7 +806,7 @@ public void run(WhisperServerConfiguration config, Environment environment) thro
if (config.getSubscription() != null && config.getOneTimeDonations() != null) {
commonControllers.add(new SubscriptionController(clock, config.getSubscription(), config.getOneTimeDonations(),
subscriptionManager, stripeManager, braintreeManager, zkReceiptOperations, issuedReceiptsManager, profileBadgeConverter,
resourceBundleLevelTranslator));
resourceBundleLevelTranslator, bankMandateTranslator));
}

for (Object controller : commonControllers) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@
import org.whispersystems.textsecuregcm.storage.IssuedReceiptsManager;
import org.whispersystems.textsecuregcm.storage.SubscriptionManager;
import org.whispersystems.textsecuregcm.storage.SubscriptionManager.GetResult;
import org.whispersystems.textsecuregcm.subscriptions.BankMandateTranslator;
import org.whispersystems.textsecuregcm.subscriptions.BankTransferType;
import org.whispersystems.textsecuregcm.subscriptions.BraintreeManager;
import org.whispersystems.textsecuregcm.subscriptions.ChargeFailure;
import org.whispersystems.textsecuregcm.subscriptions.PaymentMethod;
Expand Down Expand Up @@ -117,6 +119,7 @@ public class SubscriptionController {
private final IssuedReceiptsManager issuedReceiptsManager;
private final BadgeTranslator badgeTranslator;
private final LevelTranslator levelTranslator;
private final BankMandateTranslator bankMandateTranslator;
private static final String INVALID_ACCEPT_LANGUAGE_COUNTER_NAME = MetricsUtil.name(SubscriptionController.class,
"invalidAcceptLanguage");
private static final String RECEIPT_ISSUED_COUNTER_NAME = MetricsUtil.name(SubscriptionController.class, "receiptIssued");
Expand All @@ -135,7 +138,8 @@ public SubscriptionController(
@Nonnull ServerZkReceiptOperations zkReceiptOperations,
@Nonnull IssuedReceiptsManager issuedReceiptsManager,
@Nonnull BadgeTranslator badgeTranslator,
@Nonnull LevelTranslator levelTranslator) {
@Nonnull LevelTranslator levelTranslator,
@Nonnull BankMandateTranslator bankMandateTranslator) {
this.clock = Objects.requireNonNull(clock);
this.subscriptionConfiguration = Objects.requireNonNull(subscriptionConfiguration);
this.oneTimeDonationConfiguration = Objects.requireNonNull(oneTimeDonationConfiguration);
Expand All @@ -146,6 +150,7 @@ public SubscriptionController(
this.issuedReceiptsManager = Objects.requireNonNull(issuedReceiptsManager);
this.badgeTranslator = Objects.requireNonNull(badgeTranslator);
this.levelTranslator = Objects.requireNonNull(levelTranslator);
this.bankMandateTranslator = Objects.requireNonNull(bankMandateTranslator);
}

private Map<String, CurrencyConfiguration> buildCurrencyConfiguration(@Nullable final UserAgent userAgent) {
Expand Down Expand Up @@ -587,6 +592,20 @@ public CompletableFuture<Response> getConfiguration(@Context ContainerRequestCon
});
}

@GET
@Path("/bank_mandate/{bankTransferType}")
@Produces(MediaType.APPLICATION_JSON)
public CompletableFuture<Response> getBankMandate(final @Context ContainerRequestContext containerRequestContext,
final @PathParam("bankTransferType") BankTransferType bankTransferType) {
return CompletableFuture.supplyAsync(() -> {
List<Locale> acceptableLanguages = getAcceptableLanguagesForRequest(containerRequestContext);
return Response.ok(new GetBankMandateResponse(
bankMandateTranslator.translate(acceptableLanguages, bankTransferType))).build();
});
}

public record GetBankMandateResponse(String mandate) {}

public record GetBoostBadgesResponse(Map<Long, Level> levels) {
public record Level(PurchasableBadge badge) {
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/

package org.whispersystems.textsecuregcm.subscriptions;

import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.ResourceBundle;
import javax.annotation.Nonnull;
import org.signal.i18n.HeaderControlledResourceBundleLookup;

public class BankMandateTranslator {
private static final String BASE_NAME = "org.signal.bankmandate.BankMandate";
private final HeaderControlledResourceBundleLookup headerControlledResourceBundleLookup;

public BankMandateTranslator(
@Nonnull final HeaderControlledResourceBundleLookup headerControlledResourceBundleLookup) {
this.headerControlledResourceBundleLookup = Objects.requireNonNull(headerControlledResourceBundleLookup);
}

public String translate(final List<Locale> acceptableLanguages, final BankTransferType bankTransferType) {
final ResourceBundle resourceBundle = headerControlledResourceBundleLookup.getResourceBundle(BASE_NAME,
acceptableLanguages);
return resourceBundle.getString(getKey(bankTransferType));
}

private static String getKey(final BankTransferType bankTransferType) {
return switch (bankTransferType) {
case SEPA_DEBIT -> "SEPA_MANDATE";
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/

package org.whispersystems.textsecuregcm.subscriptions;

public enum BankTransferType {
SEPA_DEBIT
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#
# Copyright 2023 Signal Messenger, LLC
# SPDX-License-Identifier: AGPL-3.0-only
#

SEPA_MANDATE = By providing your payment information and confirming this payment, you authorise (A) Signal Technology Foundation and Stripe, our payment service provider, to send instructions to your bank to debit your account and (B) your bank to debit your account in accordance with those instructions. As part of your rights, you are entitled to a refund from your bank under the terms and conditions of your agreement with your bank. A refund must be claimed within 8 weeks starting from the date on which your account was debited. Your rights are explained in a statement that you can obtain from your bank. You agree to receive notifications for future debits up to 2 days before they occur.

Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,15 @@
import org.whispersystems.textsecuregcm.badges.LevelTranslator;
import org.whispersystems.textsecuregcm.configuration.OneTimeDonationConfiguration;
import org.whispersystems.textsecuregcm.configuration.SubscriptionConfiguration;
import org.whispersystems.textsecuregcm.controllers.SubscriptionController.GetBankMandateResponse;
import org.whispersystems.textsecuregcm.controllers.SubscriptionController.GetSubscriptionConfigurationResponse;
import org.whispersystems.textsecuregcm.entities.Badge;
import org.whispersystems.textsecuregcm.entities.BadgeSvg;
import org.whispersystems.textsecuregcm.mappers.CompletionExceptionMapper;
import org.whispersystems.textsecuregcm.mappers.SubscriptionProcessorExceptionMapper;
import org.whispersystems.textsecuregcm.storage.IssuedReceiptsManager;
import org.whispersystems.textsecuregcm.storage.SubscriptionManager;
import org.whispersystems.textsecuregcm.subscriptions.BankMandateTranslator;
import org.whispersystems.textsecuregcm.subscriptions.BraintreeManager;
import org.whispersystems.textsecuregcm.subscriptions.BraintreeManager.PayPalOneTimePaymentApprovalDetails;
import org.whispersystems.textsecuregcm.subscriptions.ChargeFailure;
Expand Down Expand Up @@ -97,9 +99,10 @@ class SubscriptionControllerTest {
private static final IssuedReceiptsManager ISSUED_RECEIPTS_MANAGER = mock(IssuedReceiptsManager.class);
private static final BadgeTranslator BADGE_TRANSLATOR = mock(BadgeTranslator.class);
private static final LevelTranslator LEVEL_TRANSLATOR = mock(LevelTranslator.class);
private static final BankMandateTranslator BANK_MANDATE_TRANSLATOR = mock(BankMandateTranslator.class);
private static final SubscriptionController SUBSCRIPTION_CONTROLLER = new SubscriptionController(
CLOCK, SUBSCRIPTION_CONFIG, ONETIME_CONFIG, SUBSCRIPTION_MANAGER, STRIPE_MANAGER, BRAINTREE_MANAGER, ZK_OPS,
ISSUED_RECEIPTS_MANAGER, BADGE_TRANSLATOR, LEVEL_TRANSLATOR);
ISSUED_RECEIPTS_MANAGER, BADGE_TRANSLATOR, LEVEL_TRANSLATOR, BANK_MANDATE_TRANSLATOR);
private static final ResourceExtension RESOURCE_EXTENSION = ResourceExtension.builder()
.addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE)
.addProvider(AuthHelper.getAuthFilter())
Expand Down Expand Up @@ -710,6 +713,24 @@ static Stream<Arguments> setSubscriptionLevelExistingSubscription() {
);
}

@Test
void testGetBankMandate() {
when(BANK_MANDATE_TRANSLATOR.translate(any(), any())).thenReturn("bankMandate");
final Response response = RESOURCE_EXTENSION.target("/v1/subscription/bank_mandate/sepa_debit")
.request()
.get();
assertThat(response.getStatus()).isEqualTo(200);
assertThat(response.readEntity(GetBankMandateResponse.class).mandate()).isEqualTo("bankMandate");
}

@Test
void testGetBankMandateInvalidBankTransferType() {
final Response response = RESOURCE_EXTENSION.target("/v1/subscription/ach")
.request()
.get();
assertThat(response.getStatus()).isEqualTo(404);
}

@ParameterizedTest
@MethodSource
void getSubscriptionConfiguration(final String userAgent, final boolean expectSepa) {
Expand Down

0 comments on commit e1aa734

Please sign in to comment.