From 9cfc2ba09a374fab41639ab3a786d49992779958 Mon Sep 17 00:00:00 2001 From: Katherine Date: Thu, 14 Dec 2023 19:48:29 -0500 Subject: [PATCH] Persist onetime donation payment success timestamps for Braintree transactions --- .../controllers/SubscriptionController.java | 5 +++-- .../storage/OneTimeDonationsManager.java | 16 ++++++---------- .../storage/DynamoDbExtensionSchema.java | 4 ++-- .../storage/OnetimeDonationsManagerTest.java | 2 +- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/SubscriptionController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/SubscriptionController.java index 374af9390..25827b692 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/SubscriptionController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/SubscriptionController.java @@ -780,8 +780,9 @@ public class SubscriptionController { }) .thenCompose(unused -> braintreeManager.captureOneTimePayment(request.payerId, request.paymentId, request.paymentToken, request.currency, request.amount, request.level)) - .thenApply(chargeSuccessDetails -> Response.ok( - new ConfirmPayPalBoostResponse(chargeSuccessDetails.paymentId())).build()); + .thenCompose(chargeSuccessDetails -> oneTimeDonationsManager.putPaidAt(chargeSuccessDetails.paymentId(), Instant.now())) + .thenApply(paymentId -> Response.ok( + new ConfirmPayPalBoostResponse(paymentId)).build()); } public static class CreateBoostReceiptCredentialsRequest { diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/OneTimeDonationsManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/OneTimeDonationsManager.java index 24bf29a65..b8a80d462 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/OneTimeDonationsManager.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/OneTimeDonationsManager.java @@ -7,7 +7,6 @@ package org.whispersystems.textsecuregcm.storage; import static com.codahale.metrics.MetricRegistry.name; -import com.google.common.annotations.VisibleForTesting; import io.micrometer.core.instrument.Metrics; import java.time.Instant; import java.util.Map; @@ -15,13 +14,12 @@ import java.util.Objects; import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; import org.whispersystems.textsecuregcm.util.AttributeValues; -import org.whispersystems.textsecuregcm.util.Util; import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient; import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; public class OneTimeDonationsManager { - public static final String KEY_PAYMENT_INTENT_ID = "P"; // S + public static final String KEY_PAYMENT_ID = "P"; // S public static final String ATTR_PAID_AT = "A"; // N private static final String ONETIME_DONATION_NOT_FOUND_COUNTER_NAME = name(OneTimeDonationsManager.class, "onetimeDonationNotFound"); private final String table; @@ -34,11 +32,11 @@ public class OneTimeDonationsManager { this.dynamoDbAsyncClient = Objects.requireNonNull(dynamoDbAsyncClient); } - public CompletableFuture getPaidAt(final String paymentIntentId, final Instant fallbackTimestamp) { + public CompletableFuture getPaidAt(final String paymentId, final Instant fallbackTimestamp) { final GetItemRequest getItemRequest = GetItemRequest.builder() .consistentRead(Boolean.TRUE) .tableName(table) - .key(Map.of(KEY_PAYMENT_INTENT_ID, AttributeValues.fromString(paymentIntentId))) + .key(Map.of(KEY_PAYMENT_ID, AttributeValues.fromString(paymentId))) .projectionExpression(ATTR_PAID_AT) .build(); @@ -52,15 +50,13 @@ public class OneTimeDonationsManager { }); } - @VisibleForTesting - CompletableFuture putPaidAt(final String paymentIntentId, final Instant paidAt) { + public CompletableFuture putPaidAt(final String paymentId, final Instant paidAt) { return dynamoDbAsyncClient.putItem(PutItemRequest.builder() .tableName(table) .item(Map.of( - KEY_PAYMENT_INTENT_ID, AttributeValues.fromString(paymentIntentId), + KEY_PAYMENT_ID, AttributeValues.fromString(paymentId), ATTR_PAID_AT, AttributeValues.fromLong(paidAt.getEpochSecond()))) .build()) - .thenRun(Util.NOOP); - + .thenApply(unused -> paymentId); } } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/DynamoDbExtensionSchema.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/DynamoDbExtensionSchema.java index 1fb403f6f..8381da844 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/DynamoDbExtensionSchema.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/DynamoDbExtensionSchema.java @@ -243,10 +243,10 @@ public final class DynamoDbExtensionSchema { .build())), ONETIME_DONATIONS("onetime_donations_test", - OneTimeDonationsManager.KEY_PAYMENT_INTENT_ID, + OneTimeDonationsManager.KEY_PAYMENT_ID, null, List.of(AttributeDefinition.builder() - .attributeName(OneTimeDonationsManager.KEY_PAYMENT_INTENT_ID) + .attributeName(OneTimeDonationsManager.KEY_PAYMENT_ID) .attributeType(ScalarAttributeType.S) .build()), List.of(), List.of()), diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/OnetimeDonationsManagerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/OnetimeDonationsManagerTest.java index 96587c07d..f008c5fc1 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/OnetimeDonationsManagerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/OnetimeDonationsManagerTest.java @@ -25,7 +25,7 @@ public class OnetimeDonationsManagerTest { } @Test - void testGetPaidAtTimestamp() { + void testSetGetPaidAtTimestamp() { final String validPaymentIntentId = "abc"; final Instant paidAt = Instant.ofEpochSecond(1_000_000); final Instant fallBackTimestamp = Instant.ofEpochSecond(2_000_000);