Add RedeemReceiptRequest object and DonationService.

This commit is contained in:
Alex Hart
2021-09-30 17:06:13 -03:00
committed by Greyson Parrelli
parent 891dfc1b68
commit 7f3ba1978d
6 changed files with 137 additions and 1 deletions

View File

@@ -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());
}
}

View File

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

View File

@@ -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
{

View File

@@ -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;
}
}