Use last subscription created at time as a subscription generation number

This commit is contained in:
Ehren Kret 2021-10-14 12:06:19 -05:00
parent c0837104cd
commit 8b8c6237be
2 changed files with 12 additions and 7 deletions

View File

@ -305,11 +305,12 @@ public class SubscriptionController {
} }
if (record.subscriptionId == null) { if (record.subscriptionId == null) {
long lastSubscriptionCreatedAt = record.subscriptionCreatedAt != null ? record.subscriptionCreatedAt.getEpochSecond() : 0;
// we don't have one yet so create it and then record the subscription id // we don't have one yet so create it and then record the subscription id
// //
// this relies on stripe's idempotency key to avoid creating more than one subscription if the client // this relies on stripe's idempotency key to avoid creating more than one subscription if the client
// retries this request // retries this request
return stripeManager.createSubscription(record.customerId, priceConfiguration.getId(), level) return stripeManager.createSubscription(record.customerId, priceConfiguration.getId(), level, lastSubscriptionCreatedAt)
.thenCompose(subscription -> subscriptionManager.subscriptionCreated( .thenCompose(subscription -> subscriptionManager.subscriptionCreated(
requestData.subscriberUser, subscription.getId(), requestData.now, level) requestData.subscriberUser, subscription.getId(), requestData.now, level)
.thenApply(unused -> subscription)); .thenApply(unused -> subscription));

View File

@ -50,6 +50,7 @@ import javax.annotation.Nullable;
import javax.crypto.Mac; import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.binary.Hex;
import org.whispersystems.textsecuregcm.util.Conversions;
public class StripeManager { public class StripeManager {
@ -139,7 +140,7 @@ public class StripeManager {
}, executor); }, executor);
} }
public CompletableFuture<Subscription> createSubscription(String customerId, String priceId, long level) { public CompletableFuture<Subscription> createSubscription(String customerId, String priceId, long level, long lastSubscriptionCreatedAt) {
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
SubscriptionCreateParams params = SubscriptionCreateParams.builder() SubscriptionCreateParams params = SubscriptionCreateParams.builder()
.setCustomer(customerId) .setCustomer(customerId)
@ -152,9 +153,9 @@ public class StripeManager {
// the idempotency key intentionally excludes priceId // the idempotency key intentionally excludes priceId
// //
// If the client tells the server several times in a row before the initial creation of a subscription to // If the client tells the server several times in a row before the initial creation of a subscription to
// create a subscription, we want to ensure only one gets created. If the prices are different each time, // create a subscription, we want to ensure only one gets created.
// whichever one gets to stripe first will win (depending on how idempotent the idempotency keys are...) return Subscription.create(params, commonOptions(generateIdempotencyKeyForCreateSubscription(
return Subscription.create(params, commonOptions(generateIdempotencyKeyForCustomerId(customerId))); customerId, lastSubscriptionCreatedAt)));
} catch (StripeException e) { } catch (StripeException e) {
throw new CompletionException(e); throw new CompletionException(e);
} }
@ -332,8 +333,11 @@ public class StripeManager {
return generateIdempotencyKey("subscriberUser", mac -> mac.update(subscriberUser)); return generateIdempotencyKey("subscriberUser", mac -> mac.update(subscriberUser));
} }
private String generateIdempotencyKeyForCustomerId(String customerId) { private String generateIdempotencyKeyForCreateSubscription(String customerId, long lastSubscriptionCreatedAt) {
return generateIdempotencyKey("customerId", mac -> mac.update(customerId.getBytes(StandardCharsets.UTF_8))); return generateIdempotencyKey("customerId", mac -> {
mac.update(customerId.getBytes(StandardCharsets.UTF_8));
mac.update(Conversions.longToByteArray(lastSubscriptionCreatedAt));
});
} }
private String generateIdempotencyKey(String type, Consumer<Mac> byteConsumer) { private String generateIdempotencyKey(String type, Consumer<Mac> byteConsumer) {