mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-27 04:04:43 +01:00
Implement refactor to utilize new donation configuration endpoint.
This commit is contained in:
committed by
Cody Henthorne
parent
40cf87307a
commit
424a0233c2
@@ -6,7 +6,6 @@ import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialPresentation;
|
||||
import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialRequest;
|
||||
import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialResponse;
|
||||
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
|
||||
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
|
||||
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription;
|
||||
import org.whispersystems.signalservice.api.subscriptions.PayPalConfirmPaymentIntentResponse;
|
||||
@@ -14,21 +13,19 @@ import org.whispersystems.signalservice.api.subscriptions.PayPalCreatePaymentInt
|
||||
import org.whispersystems.signalservice.api.subscriptions.PayPalCreatePaymentMethodResponse;
|
||||
import org.whispersystems.signalservice.api.subscriptions.StripeClientSecret;
|
||||
import org.whispersystems.signalservice.api.subscriptions.SubscriberId;
|
||||
import org.whispersystems.signalservice.api.subscriptions.SubscriptionLevels;
|
||||
import org.whispersystems.signalservice.api.util.CredentialsProvider;
|
||||
import org.whispersystems.signalservice.internal.EmptyResponse;
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse;
|
||||
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
|
||||
import org.whispersystems.signalservice.internal.push.DonationProcessor;
|
||||
import org.whispersystems.signalservice.internal.push.DonationsConfiguration;
|
||||
import org.whispersystems.signalservice.internal.push.PushServiceSocket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import io.reactivex.rxjava3.annotations.NonNull;
|
||||
|
||||
@@ -41,6 +38,20 @@ public class DonationsService {
|
||||
|
||||
private final PushServiceSocket pushServiceSocket;
|
||||
|
||||
private final AtomicReference<CacheEntry> donationsConfigurationCache = new AtomicReference<>(null);
|
||||
|
||||
private static class CacheEntry {
|
||||
private final DonationsConfiguration donationsConfiguration;
|
||||
private final long expiresAt;
|
||||
private final Locale locale;
|
||||
|
||||
private CacheEntry(DonationsConfiguration donationsConfiguration, long expiresAt, Locale locale) {
|
||||
this.donationsConfiguration = donationsConfiguration;
|
||||
this.expiresAt = expiresAt;
|
||||
this.locale = locale;
|
||||
}
|
||||
}
|
||||
|
||||
public DonationsService(
|
||||
SignalServiceConfiguration configuration,
|
||||
CredentialsProvider credentialsProvider,
|
||||
@@ -95,61 +106,24 @@ public class DonationsService {
|
||||
return wrapInServiceResponse(() -> new Pair<>(pushServiceSocket.submitBoostReceiptCredentials(paymentIntentId, receiptCredentialRequest, processor), 200));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The suggested amounts for Signal Boost
|
||||
*/
|
||||
public ServiceResponse<Map<String, List<BigDecimal>>> getBoostAmounts() {
|
||||
return wrapInServiceResponse(() -> new Pair<>(pushServiceSocket.getBoostAmounts(), 200));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The badge configuration for signal boost. Expect for right now only a single level numbered 1.
|
||||
*/
|
||||
public ServiceResponse<SignalServiceProfile.Badge> getBoostBadge(Locale locale) {
|
||||
return wrapInServiceResponse(() -> new Pair<>(pushServiceSocket.getBoostLevels(locale).getLevels().get(SubscriptionLevels.BOOST_LEVEL).getBadge(), 200));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A specific gift badge, by level.
|
||||
*/
|
||||
public ServiceResponse<SignalServiceProfile.Badge> getGiftBadge(Locale locale, long level) {
|
||||
return wrapInServiceResponse(() -> new Pair<>(pushServiceSocket.getBoostLevels(locale).getLevels().get(String.valueOf(level)).getBadge(), 200));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return All gift badges the server currently has available.
|
||||
*/
|
||||
public ServiceResponse<Map<Long, SignalServiceProfile.Badge>> getGiftBadges(Locale locale) {
|
||||
return wrapInServiceResponse(() -> {
|
||||
Map<String, SubscriptionLevels.Level> levels = pushServiceSocket.getBoostLevels(locale).getLevels();
|
||||
Map<Long, SignalServiceProfile.Badge> badges = new TreeMap<>();
|
||||
|
||||
for (Map.Entry<String, SubscriptionLevels.Level> levelEntry : levels.entrySet()) {
|
||||
if (!Objects.equals(levelEntry.getKey(), SubscriptionLevels.BOOST_LEVEL)) {
|
||||
try {
|
||||
badges.put(Long.parseLong(levelEntry.getKey()), levelEntry.getValue().getBadge());
|
||||
} catch (NumberFormatException e) {
|
||||
Log.w(TAG, "Could not parse gift badge for level entry " + levelEntry.getKey(), e);
|
||||
}
|
||||
public ServiceResponse<DonationsConfiguration> getDonationsConfiguration(Locale locale) {
|
||||
CacheEntry cacheEntryOutsideLock = donationsConfigurationCache.get();
|
||||
if (isNewCacheEntryRequired(cacheEntryOutsideLock, locale)) {
|
||||
synchronized (this) {
|
||||
CacheEntry cacheEntryInLock = donationsConfigurationCache.get();
|
||||
if (isNewCacheEntryRequired(cacheEntryInLock, locale)) {
|
||||
return wrapInServiceResponse(() -> {
|
||||
DonationsConfiguration donationsConfiguration = pushServiceSocket.getDonationsConfiguration(locale);
|
||||
donationsConfigurationCache.set(new CacheEntry(donationsConfiguration, System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1), locale));
|
||||
return new Pair<>(donationsConfiguration, 200);
|
||||
});
|
||||
} else {
|
||||
return wrapInServiceResponse(() -> new Pair<>(cacheEntryOutsideLock.donationsConfiguration, 200));
|
||||
}
|
||||
}
|
||||
|
||||
return new Pair<>(badges, 200);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amounts for the gift badge.
|
||||
*/
|
||||
public ServiceResponse<Map<String, BigDecimal>> getGiftAmount() {
|
||||
return wrapInServiceResponse(() -> new Pair<>(pushServiceSocket.getGiftAmount(), 200));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subscription levels that are available for the client to choose from along with currencies and current prices
|
||||
*/
|
||||
public ServiceResponse<SubscriptionLevels> getSubscriptionLevels(Locale locale) {
|
||||
return wrapInServiceResponse(() -> new Pair<>(pushServiceSocket.getSubscriptionLevels(locale), 200));
|
||||
} else {
|
||||
return wrapInServiceResponse(() -> new Pair<>(cacheEntryOutsideLock.donationsConfiguration, 200));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,6 +338,10 @@ public class DonationsService {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNewCacheEntryRequired(CacheEntry cacheEntry, Locale locale) {
|
||||
return cacheEntry == null || cacheEntry.expiresAt < System.currentTimeMillis() || !Objects.equals(locale, cacheEntry.locale);
|
||||
}
|
||||
|
||||
private interface Producer<T> {
|
||||
Pair<T, Integer> produce() throws IOException;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package org.whispersystems.signalservice.internal.push;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Response JSON for a call to /v1/subscriptions/configuration
|
||||
*/
|
||||
public class DonationsConfiguration {
|
||||
|
||||
public static final int BOOST_LEVEL = 1;
|
||||
public static final int GIFT_LEVEL = 100;
|
||||
public static final HashSet<Integer> SUBSCRIPTION_LEVELS = new HashSet<>(Arrays.asList(500, 1000, 2000));
|
||||
|
||||
@JsonProperty("currencies")
|
||||
private Map<String, CurrencyConfiguration> currencies;
|
||||
|
||||
@JsonProperty("levels")
|
||||
private Map<Integer, LevelConfiguration> levels;
|
||||
|
||||
public static class CurrencyConfiguration {
|
||||
@JsonProperty("minimum")
|
||||
private BigDecimal minimum;
|
||||
|
||||
@JsonProperty("oneTime")
|
||||
private Map<Integer, List<BigDecimal>> oneTime;
|
||||
|
||||
@JsonProperty("subscription")
|
||||
private Map<Integer, BigDecimal> subscription;
|
||||
|
||||
@JsonProperty("supportedPaymentMethods")
|
||||
private Set<String> supportedPaymentMethods;
|
||||
|
||||
public BigDecimal getMinimum() {
|
||||
return minimum;
|
||||
}
|
||||
|
||||
public Map<Integer, List<BigDecimal>> getOneTime() {
|
||||
return oneTime;
|
||||
}
|
||||
|
||||
public Map<Integer, BigDecimal> getSubscription() {
|
||||
return subscription;
|
||||
}
|
||||
|
||||
public Set<String> getSupportedPaymentMethods() {
|
||||
return supportedPaymentMethods;
|
||||
}
|
||||
}
|
||||
|
||||
public static class LevelConfiguration {
|
||||
@JsonProperty("name")
|
||||
private String name;
|
||||
|
||||
@JsonProperty("badge")
|
||||
private SignalServiceProfile.Badge badge;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public SignalServiceProfile.Badge getBadge() {
|
||||
return badge;
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, CurrencyConfiguration> getCurrencies() {
|
||||
return currencies;
|
||||
}
|
||||
|
||||
public Map<Integer, LevelConfiguration> getLevels() {
|
||||
return levels;
|
||||
}
|
||||
}
|
||||
@@ -264,7 +264,6 @@ public class PushServiceSocket {
|
||||
|
||||
private static final String DONATION_REDEEM_RECEIPT = "/v1/donation/redeem-receipt";
|
||||
|
||||
private static final String SUBSCRIPTION_LEVELS = "/v1/subscription/levels";
|
||||
private static final String UPDATE_SUBSCRIPTION_LEVEL = "/v1/subscription/%s/level/%s/%s/%s";
|
||||
private static final String SUBSCRIPTION = "/v1/subscription/%s";
|
||||
private static final String CREATE_STRIPE_SUBSCRIPTION_PAYMENT_METHOD = "/v1/subscription/%s/create_payment_method";
|
||||
@@ -272,13 +271,11 @@ public class PushServiceSocket {
|
||||
private static final String DEFAULT_STRIPE_SUBSCRIPTION_PAYMENT_METHOD = "/v1/subscription/%s/default_payment_method/%s";
|
||||
private static final String DEFAULT_PAYPAL_SUBSCRIPTION_PAYMENT_METHOD = "/v1/subscription/%s/default_payment_method/paypal/%s";
|
||||
private static final String SUBSCRIPTION_RECEIPT_CREDENTIALS = "/v1/subscription/%s/receipt_credentials";
|
||||
private static final String BOOST_AMOUNTS = "/v1/subscription/boost/amounts";
|
||||
private static final String GIFT_AMOUNT = "/v1/subscription/boost/amounts/gift";
|
||||
private static final String CREATE_STRIPE_ONE_TIME_PAYMENT_INTENT = "/v1/subscription/boost/create";
|
||||
private static final String CREATE_PAYPAL_ONE_TIME_PAYMENT_INTENT = "/v1/subscription/boost/paypal/create";
|
||||
private static final String CONFIRM_PAYPAL_ONE_TIME_PAYMENT_INTENT = "/v1/subscription/boost/paypal/confirm";
|
||||
private static final String BOOST_RECEIPT_CREDENTIALS = "/v1/subscription/boost/receipt_credentials";
|
||||
private static final String BOOST_BADGES = "/v1/subscription/boost/badges";
|
||||
private static final String DONATIONS_CONFIGURATION = "/v1/subscription/configuration";
|
||||
|
||||
private static final String CDSI_AUTH = "/v2/directory/auth";
|
||||
|
||||
@@ -1048,28 +1045,10 @@ public class PushServiceSocket {
|
||||
public PayPalCreatePaymentMethodResponse createPayPalPaymentMethod(Locale locale, String subscriberId, String returnUrl, String cancelUrl) throws IOException {
|
||||
Map<String, String> headers = Collections.singletonMap("Accept-Language", locale.getLanguage() + "-" + locale.getCountry());
|
||||
String payload = JsonUtil.toJson(new PayPalCreatePaymentMethodPayload(returnUrl, cancelUrl));
|
||||
String result = makeServiceRequestWithoutAuthentication(String.format(CREATE_PAYPAL_SUBSCRIPTION_PAYMENT_METHOD, subscriberId), "POST", payload);
|
||||
String result = makeServiceRequestWithoutAuthentication(String.format(CREATE_PAYPAL_SUBSCRIPTION_PAYMENT_METHOD, subscriberId), "POST", payload, headers, NO_HANDLER);
|
||||
return JsonUtil.fromJsonResponse(result, PayPalCreatePaymentMethodResponse.class);
|
||||
}
|
||||
|
||||
|
||||
public Map<String, List<BigDecimal>> getBoostAmounts() throws IOException {
|
||||
String result = makeServiceRequestWithoutAuthentication(BOOST_AMOUNTS, "GET", null);
|
||||
TypeReference<HashMap<String, List<BigDecimal>>> typeRef = new TypeReference<HashMap<String, List<BigDecimal>>>() {};
|
||||
return JsonUtil.fromJsonResponse(result, typeRef);
|
||||
}
|
||||
|
||||
public Map<String, BigDecimal> getGiftAmount() throws IOException {
|
||||
String result = makeServiceRequestWithoutAuthentication(GIFT_AMOUNT, "GET", null);
|
||||
TypeReference<HashMap<String, BigDecimal>> typeRef = new TypeReference<HashMap<String, BigDecimal>>() {};
|
||||
return JsonUtil.fromJsonResponse(result, typeRef);
|
||||
}
|
||||
|
||||
public SubscriptionLevels getBoostLevels(Locale locale) throws IOException {
|
||||
String result = makeServiceRequestWithoutAuthentication(BOOST_BADGES, "GET", null, AcceptLanguagesUtil.getHeadersWithAcceptLanguage(locale), NO_HANDLER);
|
||||
return JsonUtil.fromJsonResponse(result, SubscriptionLevels.class);
|
||||
}
|
||||
|
||||
public ReceiptCredentialResponse submitBoostReceiptCredentials(String paymentIntentId, ReceiptCredentialRequest receiptCredentialRequest, DonationProcessor processor) throws IOException {
|
||||
String payload = JsonUtil.toJson(new BoostReceiptCredentialRequestJson(paymentIntentId, receiptCredentialRequest, processor));
|
||||
String response = makeServiceRequestWithoutAuthentication(
|
||||
@@ -1089,10 +1068,14 @@ public class PushServiceSocket {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the DonationsConfiguration pointed at by /v1/subscriptions/configuration
|
||||
*/
|
||||
public DonationsConfiguration getDonationsConfiguration(Locale locale) throws IOException {
|
||||
Map<String, String> headers = Collections.singletonMap("Accept-Language", locale.getLanguage() + "-" + locale.getCountry());
|
||||
String result = makeServiceRequestWithoutAuthentication(DONATIONS_CONFIGURATION, "GET", null, headers, NO_HANDLER);
|
||||
|
||||
public SubscriptionLevels getSubscriptionLevels(Locale locale) throws IOException {
|
||||
String result = makeServiceRequestWithoutAuthentication(SUBSCRIPTION_LEVELS, "GET", null, AcceptLanguagesUtil.getHeadersWithAcceptLanguage(locale), NO_HANDLER);
|
||||
return JsonUtil.fromJsonResponse(result, SubscriptionLevels.class);
|
||||
return JsonUtil.fromJson(result, DonationsConfiguration.class);
|
||||
}
|
||||
|
||||
public void updateSubscriptionLevel(String subscriberId, String level, String currencyCode, String idempotencyKey) throws IOException {
|
||||
|
||||
Reference in New Issue
Block a user