Add RedeemReceiptRequest object and DonationService.

fork-5.53.8
Alex Hart 2021-09-30 17:06:13 -03:00 zatwierdzone przez Greyson Parrelli
rodzic 891dfc1b68
commit 7f3ba1978d
6 zmienionych plików z 137 dodań i 1 usunięć

Wyświetl plik

@ -48,6 +48,7 @@ import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.SignalWebSocket;
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
import org.whispersystems.signalservice.api.services.DonationsService;
import okhttp3.OkHttpClient;
@ -104,6 +105,7 @@ public class ApplicationDependencies {
private static volatile GiphyMp4Cache giphyMp4Cache;
private static volatile SimpleExoPlayerPool exoPlayerPool;
private static volatile AudioManagerCompat audioManagerCompat;
private static volatile DonationsService donationsService;
@MainThread
public static void init(@NonNull Application application, @NonNull Provider provider) {
@ -590,6 +592,17 @@ public class ApplicationDependencies {
return audioManagerCompat;
}
public static @NonNull DonationsService getDonationsService() {
if (donationsService == null) {
synchronized (LOCK) {
if (donationsService == null) {
donationsService = provider.provideDonationsService();
}
}
}
return donationsService;
}
public interface Provider {
@NonNull GroupsV2Operations provideGroupsV2Operations();
@NonNull SignalServiceAccountManager provideSignalServiceAccountManager();
@ -625,5 +638,6 @@ public class ApplicationDependencies {
@NonNull GiphyMp4Cache provideGiphyMp4Cache();
@NonNull SimpleExoPlayerPool provideExoPlayerPool();
@NonNull AudioManagerCompat provideAndroidCallAudioManager();
@NonNull DonationsService provideDonationsService();
}
}

Wyświetl plik

@ -68,6 +68,7 @@ import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.SignalWebSocket;
import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations;
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
import org.whispersystems.signalservice.api.services.DonationsService;
import org.whispersystems.signalservice.api.util.CredentialsProvider;
import org.whispersystems.signalservice.api.util.SleepTimer;
import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
@ -301,6 +302,15 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr
return AudioManagerCompat.create(context);
}
@Override
public @NonNull DonationsService provideDonationsService() {
return new DonationsService(provideSignalServiceNetworkAccess().getConfiguration(context),
new DynamicCredentialsProvider(context),
BuildConfig.SIGNAL_AGENT,
provideGroupsV2Operations(),
FeatureFlags.okHttpAutomaticRetry());
}
private @NonNull WebSocketFactory provideWebSocketFactory(@NonNull SignalWebSocketHealthMonitor healthMonitor) {
return new WebSocketFactory() {
@Override

Wyświetl plik

@ -0,0 +1,48 @@
package org.whispersystems.signalservice.api.services;
import org.signal.zkgroup.receipts.ReceiptCredentialPresentation;
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
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.PushServiceSocket;
import io.reactivex.rxjava3.core.Scheduler;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.schedulers.Schedulers;
/**
* One-stop shop for Signal service calls related to donations.
*/
public class DonationsService {
private final PushServiceSocket pushServiceSocket;
public DonationsService(
SignalServiceConfiguration configuration,
CredentialsProvider credentialsProvider,
String signalAgent,
GroupsV2Operations groupsV2Operations,
boolean automaticNetworkRetry
) {
this.pushServiceSocket = new PushServiceSocket(configuration, credentialsProvider, signalAgent, groupsV2Operations.getProfileOperations(), automaticNetworkRetry);
}
/**
* Allows a user to redeem a given receipt they were given after submitting a donation successfully.
*
* @param receiptCredentialPresentation Receipt
* @param visible Whether the badge will be visible on the user's profile immediately after redemption
* @param primary Whether the badge will be made primary immediately after redemption
*/
public Single<ServiceResponse<EmptyResponse>> redeemReceipt(ReceiptCredentialPresentation receiptCredentialPresentation, boolean visible, boolean primary) {
return Single.fromCallable(() -> {
try {
pushServiceSocket.redeemDonationReceipt(receiptCredentialPresentation, visible, primary);
return ServiceResponse.forResult(EmptyResponse.INSTANCE, 200, null);
} catch (Exception e) {
return ServiceResponse.<EmptyResponse>forUnknownError(e);
}
}).subscribeOn(Schedulers.io());
}
}

Wyświetl plik

@ -0,0 +1,8 @@
package org.whispersystems.signalservice.internal;
/**
* Indicates that no data is returned from a given service call.
*/
public enum EmptyResponse {
INSTANCE
}

Wyświetl plik

@ -25,6 +25,7 @@ import org.signal.zkgroup.profiles.ProfileKeyCredentialRequest;
import org.signal.zkgroup.profiles.ProfileKeyCredentialRequestContext;
import org.signal.zkgroup.profiles.ProfileKeyCredentialResponse;
import org.signal.zkgroup.profiles.ProfileKeyVersion;
import org.signal.zkgroup.receipts.ReceiptCredentialPresentation;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.ecc.ECPublicKey;
import org.whispersystems.libsignal.logging.Log;
@ -134,7 +135,6 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Future;
@ -233,6 +233,8 @@ public class PushServiceSocket {
private static final String SUBMIT_RATE_LIMIT_CHALLENGE = "/v1/challenge";
private static final String REQUEST_RATE_LIMIT_PUSH_CHALLENGE = "/v1/challenge/push";
private static final String DONATION_REDEEM_RECEIPT = "/v1/donation/redeem-receipt";
private static final String REPORT_SPAM = "/v1/messages/report/%s/%s";
private static final String SERVER_DELIVERED_TIMESTAMP_HEADER = "X-Signal-Timestamp";
@ -862,6 +864,11 @@ public class PushServiceSocket {
makeServiceRequest(SUBMIT_RATE_LIMIT_CHALLENGE, "PUT", payload);
}
public void redeemDonationReceipt(ReceiptCredentialPresentation receiptCredentialPresentation, boolean visible, boolean primary) throws IOException {
String payload = JsonUtil.toJson(new RedeemReceiptRequest(Base64.encodeBytesToBytes(receiptCredentialPresentation.serialize()), visible, primary));
makeServiceRequest(DONATION_REDEEM_RECEIPT, "PUT", payload);
}
public List<ContactTokenDetails> retrieveDirectory(Set<String> contactTokens)
throws NonSuccessfulResponseCodeException, PushNetworkException, MalformedResponseException
{

Wyświetl plik

@ -0,0 +1,49 @@
package org.whispersystems.signalservice.internal.push;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.signal.zkgroup.receipts.ReceiptCredentialPresentation;
import org.whispersystems.libsignal.util.guava.Preconditions;
/**
* POST /v1/donation/redeem-receipt
*
* Request object for redeeming a receipt from a donation transaction.
*/
class RedeemReceiptRequest {
private final byte[] receiptCredentialPresentation;
private final boolean visible;
private final boolean primary;
/**
* @param receiptCredentialPresentation base64-encoded no-newlines standard-character-set with-padding of the bytes of a {@link ReceiptCredentialPresentation} object
* @param visible boolean indicating if the new badge should be visible or not on the profile
* @param primary boolean indicating if the new badge should be primary or not on the profile; is always treated as false if `visible` is false
*/
@JsonCreator
RedeemReceiptRequest(
@JsonProperty("receiptCredentialPresentation") byte[] receiptCredentialPresentation,
@JsonProperty("visible") boolean visible,
@JsonProperty("primary") boolean primary) {
Preconditions.checkArgument(receiptCredentialPresentation.length == ReceiptCredentialPresentation.SIZE);
this.receiptCredentialPresentation = receiptCredentialPresentation;
this.visible = visible;
this.primary = primary;
}
public byte[] getReceiptCredentialPresentation() {
return receiptCredentialPresentation;
}
public boolean isVisible() {
return visible;
}
public boolean isPrimary() {
return primary;
}
}