mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-19 10:38:03 +01:00
Add PayPalLineItemInput with localized description
This commit is contained in:
@@ -257,6 +257,7 @@ import org.whispersystems.textsecuregcm.subscriptions.AppleAppStoreClient;
|
||||
import org.whispersystems.textsecuregcm.subscriptions.AppleAppStoreManager;
|
||||
import org.whispersystems.textsecuregcm.subscriptions.BankMandateTranslator;
|
||||
import org.whispersystems.textsecuregcm.subscriptions.BraintreeManager;
|
||||
import org.whispersystems.textsecuregcm.subscriptions.PayPalDonationsTranslator;
|
||||
import org.whispersystems.textsecuregcm.subscriptions.GooglePlayBillingManager;
|
||||
import org.whispersystems.textsecuregcm.subscriptions.StripeManager;
|
||||
import org.whispersystems.textsecuregcm.telephony.CarrierDataProvider;
|
||||
@@ -419,6 +420,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
ConfiguredProfileBadgeConverter profileBadgeConverter = new ConfiguredProfileBadgeConverter(
|
||||
clock, config.getBadges(), headerControlledResourceBundleLookup);
|
||||
BankMandateTranslator bankMandateTranslator = new BankMandateTranslator(headerControlledResourceBundleLookup);
|
||||
PayPalDonationsTranslator payPalDonationsTranslator =
|
||||
new PayPalDonationsTranslator(headerControlledResourceBundleLookup);
|
||||
|
||||
environment.lifecycle().manage(new ManagedAwsCrt());
|
||||
|
||||
@@ -1135,7 +1138,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
subscriptionManager, stripeManager, braintreeManager, googlePlayBillingManager, appleAppStoreManager,
|
||||
profileBadgeConverter, bankMandateTranslator, dynamicConfigurationManager));
|
||||
commonControllers.add(new OneTimeDonationController(clock, config.getOneTimeDonations(), stripeManager, braintreeManager,
|
||||
zkReceiptOperations, issuedReceiptsManager, oneTimeDonationsManager));
|
||||
payPalDonationsTranslator, zkReceiptOperations, issuedReceiptsManager, oneTimeDonationsManager));
|
||||
}
|
||||
|
||||
for (Object controller : commonControllers) {
|
||||
|
||||
@@ -36,6 +36,7 @@ import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -57,6 +58,7 @@ import org.whispersystems.textsecuregcm.storage.IssuedReceiptsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.OneTimeDonationsManager;
|
||||
import org.whispersystems.textsecuregcm.subscriptions.BraintreeManager;
|
||||
import org.whispersystems.textsecuregcm.subscriptions.ChargeFailure;
|
||||
import org.whispersystems.textsecuregcm.subscriptions.PayPalDonationsTranslator;
|
||||
import org.whispersystems.textsecuregcm.subscriptions.CustomerAwareSubscriptionPaymentProcessor;
|
||||
import org.whispersystems.textsecuregcm.subscriptions.PaymentDetails;
|
||||
import org.whispersystems.textsecuregcm.subscriptions.PaymentMethod;
|
||||
@@ -90,6 +92,7 @@ public class OneTimeDonationController {
|
||||
private final OneTimeDonationConfiguration oneTimeDonationConfiguration;
|
||||
private final StripeManager stripeManager;
|
||||
private final BraintreeManager braintreeManager;
|
||||
private final PayPalDonationsTranslator payPalDonationsTranslator;
|
||||
private final ServerZkReceiptOperations zkReceiptOperations;
|
||||
private final IssuedReceiptsManager issuedReceiptsManager;
|
||||
private final OneTimeDonationsManager oneTimeDonationsManager;
|
||||
@@ -99,6 +102,7 @@ public class OneTimeDonationController {
|
||||
@Nonnull OneTimeDonationConfiguration oneTimeDonationConfiguration,
|
||||
@Nonnull StripeManager stripeManager,
|
||||
@Nonnull BraintreeManager braintreeManager,
|
||||
@Nonnull PayPalDonationsTranslator payPalDonationsTranslator,
|
||||
@Nonnull ServerZkReceiptOperations zkReceiptOperations,
|
||||
@Nonnull IssuedReceiptsManager issuedReceiptsManager,
|
||||
@Nonnull OneTimeDonationsManager oneTimeDonationsManager) {
|
||||
@@ -106,6 +110,7 @@ public class OneTimeDonationController {
|
||||
this.oneTimeDonationConfiguration = Objects.requireNonNull(oneTimeDonationConfiguration);
|
||||
this.stripeManager = Objects.requireNonNull(stripeManager);
|
||||
this.braintreeManager = Objects.requireNonNull(braintreeManager);
|
||||
this.payPalDonationsTranslator = Objects.requireNonNull(payPalDonationsTranslator);
|
||||
this.zkReceiptOperations = Objects.requireNonNull(zkReceiptOperations);
|
||||
this.issuedReceiptsManager = Objects.requireNonNull(issuedReceiptsManager);
|
||||
this.oneTimeDonationsManager = Objects.requireNonNull(oneTimeDonationsManager);
|
||||
@@ -265,14 +270,23 @@ public class OneTimeDonationController {
|
||||
validateRequestCurrencyAmount(request, BigDecimal.valueOf(request.amount), braintreeManager);
|
||||
})
|
||||
.thenCompose(unused -> {
|
||||
final Locale locale = HeaderUtils.getAcceptableLanguagesForRequest(containerRequestContext).stream()
|
||||
final List<Locale> acceptableLanguages =
|
||||
HeaderUtils.getAcceptableLanguagesForRequest(containerRequestContext);
|
||||
|
||||
// These two localizations are a best-effort, and it's possible that the first `locale` and the localized line
|
||||
// item name will not match. We could try to align with the locales PayPal documents <https://developer.paypal.com/reference/locale-codes/#supported-locale-codes>
|
||||
// but that's a moving target, and we can hopefully have one of them be better for the user by selecting
|
||||
// independently.
|
||||
final Locale locale = acceptableLanguages.stream()
|
||||
.filter(l -> !"*".equals(l.getLanguage()))
|
||||
.findFirst()
|
||||
.orElse(Locale.US);
|
||||
final String localizedLineItemName = payPalDonationsTranslator.translate(acceptableLanguages,
|
||||
PayPalDonationsTranslator.ONE_TIME_DONATION_LINE_ITEM_KEY);
|
||||
|
||||
return braintreeManager.createOneTimePayment(request.currency.toUpperCase(Locale.ROOT), request.amount,
|
||||
locale.toLanguageTag(),
|
||||
request.returnUrl, request.cancelUrl);
|
||||
request.returnUrl, request.cancelUrl, localizedLineItemName);
|
||||
})
|
||||
.thenApply(approvalDetails -> Response.ok(
|
||||
new CreatePayPalBoostResponse(approvalDetails.approvalUrl(), approvalDetails.paymentId())).build());
|
||||
|
||||
@@ -21,12 +21,14 @@ import com.braintree.graphql.client.type.PayPalBillingAgreementInput;
|
||||
import com.braintree.graphql.client.type.PayPalExperienceProfileInput;
|
||||
import com.braintree.graphql.client.type.PayPalIntent;
|
||||
import com.braintree.graphql.client.type.PayPalLandingPageType;
|
||||
import com.braintree.graphql.client.type.PayPalLineItemInput;
|
||||
import com.braintree.graphql.client.type.PayPalOneTimePaymentInput;
|
||||
import com.braintree.graphql.client.type.PayPalProductAttributesInput;
|
||||
import com.braintree.graphql.client.type.PayPalUserAction;
|
||||
import com.braintree.graphql.client.type.TokenizePayPalBillingAgreementInput;
|
||||
import com.braintree.graphql.client.type.TokenizePayPalOneTimePaymentInput;
|
||||
import com.braintree.graphql.client.type.TransactionInput;
|
||||
import com.braintree.graphql.client.type.TransactionLineItemType;
|
||||
import com.braintree.graphql.client.type.VaultPaymentMethodInput;
|
||||
import com.braintree.graphql.clientoperation.ChargePayPalOneTimePaymentMutation;
|
||||
import com.braintree.graphql.clientoperation.CreatePayPalBillingAgreementMutation;
|
||||
@@ -77,10 +79,10 @@ class BraintreeGraphqlClient {
|
||||
|
||||
CompletableFuture<CreatePayPalOneTimePaymentMutation.CreatePayPalOneTimePayment> createPayPalOneTimePayment(
|
||||
final BigDecimal amount, final String currency, final String returnUrl,
|
||||
final String cancelUrl, final String locale) {
|
||||
final String cancelUrl, final String locale, final String localizedLineItemName) {
|
||||
|
||||
final CreatePayPalOneTimePaymentInput input = buildCreatePayPalOneTimePaymentInput(amount, currency, returnUrl,
|
||||
cancelUrl, locale);
|
||||
cancelUrl, locale, localizedLineItemName);
|
||||
final CreatePayPalOneTimePaymentMutation mutation = new CreatePayPalOneTimePaymentMutation(input);
|
||||
final HttpRequest request = buildRequest(mutation);
|
||||
|
||||
@@ -104,7 +106,12 @@ class BraintreeGraphqlClient {
|
||||
}
|
||||
|
||||
private static CreatePayPalOneTimePaymentInput buildCreatePayPalOneTimePaymentInput(BigDecimal amount,
|
||||
String currency, String returnUrl, String cancelUrl, String locale) {
|
||||
String currency, String returnUrl, String cancelUrl, String locale, String localizedLineItemName) {
|
||||
|
||||
// Note locale and localizedLineItemName are a best-effort, and it's possible that they will not match.
|
||||
// We could try to align with the locales PayPal documents <https://developer.paypal.com/reference/locale-codes/#supported-locale-codes>
|
||||
// but that's a moving target, and we can hopefully have one of them be better for the user by selecting
|
||||
// independently.
|
||||
|
||||
return new CreatePayPalOneTimePaymentInput(
|
||||
Optional.absent(),
|
||||
@@ -113,7 +120,17 @@ class BraintreeGraphqlClient {
|
||||
cancelUrl,
|
||||
Optional.absent(),
|
||||
PayPalIntent.SALE,
|
||||
Optional.absent(),
|
||||
Optional.present(List.of(
|
||||
new PayPalLineItemInput(
|
||||
localizedLineItemName,
|
||||
1, // quantity, always 1
|
||||
amount.toString(),
|
||||
TransactionLineItemType.DEBIT,
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
0, // unitTaxAmount, always zero
|
||||
Optional.absent()
|
||||
))),
|
||||
Optional.present(false), // offerPayLater,
|
||||
Optional.absent(),
|
||||
Optional.present(
|
||||
|
||||
@@ -149,10 +149,10 @@ public class BraintreeManager implements CustomerAwareSubscriptionPaymentProcess
|
||||
}
|
||||
|
||||
public CompletableFuture<PayPalOneTimePaymentApprovalDetails> createOneTimePayment(String currency, long amount,
|
||||
String locale, String returnUrl, String cancelUrl) {
|
||||
String locale, String returnUrl, String cancelUrl, String localizedLineItemname) {
|
||||
return braintreeGraphqlClient.createPayPalOneTimePayment(convertApiAmountToBraintreeAmount(currency, amount),
|
||||
currency.toUpperCase(Locale.ROOT), returnUrl,
|
||||
cancelUrl, locale)
|
||||
cancelUrl, locale, localizedLineItemname)
|
||||
.thenApply(result -> new PayPalOneTimePaymentApprovalDetails((String) result.approvalUrl, result.paymentId));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2026 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 PayPalDonationsTranslator {
|
||||
|
||||
public static final String ONE_TIME_DONATION_LINE_ITEM_KEY = "oneTime.donationLineItemName";
|
||||
|
||||
private static final String BASE_NAME = "org.signal.donations.PayPal";
|
||||
|
||||
private final HeaderControlledResourceBundleLookup headerControlledResourceBundleLookup;
|
||||
|
||||
public PayPalDonationsTranslator(
|
||||
@Nonnull final HeaderControlledResourceBundleLookup headerControlledResourceBundleLookup) {
|
||||
this.headerControlledResourceBundleLookup = Objects.requireNonNull(headerControlledResourceBundleLookup);
|
||||
}
|
||||
|
||||
public String translate(final List<Locale> acceptableLanguages, final String key) {
|
||||
final ResourceBundle resourceBundle = headerControlledResourceBundleLookup.getResourceBundle(BASE_NAME,
|
||||
acceptableLanguages);
|
||||
return resourceBundle.getString(key);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user