mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-20 05:38:04 +01:00
Cancel play subscriptions when replacing them
This commit is contained in:
committed by
ravi-signal
parent
e9b3e15556
commit
0e552bd602
@@ -420,6 +420,10 @@ public class SubscriptionController {
|
||||
|
||||
After calling this method, the payment is confirmed. Callers must durably store their subscriberId before calling
|
||||
this method to ensure their payment is tracked.
|
||||
|
||||
Once a purchaseToken to is posted to a subscriberId, the same subscriberId must not be used with another payment
|
||||
method. A different playbilling purchaseToken can be posted to the same subscriberId, in this case the subscription
|
||||
associated with the old purchaseToken will be cancelled.
|
||||
""")
|
||||
@ApiResponse(responseCode = "200", description = "The purchaseToken was validated and acknowledged")
|
||||
@ApiResponse(responseCode = "402", description = "The purchaseToken payment is incomplete or invalid")
|
||||
|
||||
@@ -349,29 +349,42 @@ public class SubscriptionManager {
|
||||
final GooglePlayBillingManager googlePlayBillingManager,
|
||||
final String purchaseToken) {
|
||||
|
||||
return getSubscriber(subscriberCredentials).thenCompose(record -> {
|
||||
if (record.processorCustomer != null
|
||||
&& record.processorCustomer.processor() != PaymentProvider.GOOGLE_PLAY_BILLING) {
|
||||
return CompletableFuture.failedFuture(
|
||||
new SubscriptionException.ProcessorConflict("existing processor does not match"));
|
||||
}
|
||||
// For IAP providers, the subscriptionId and the customerId are both just the purchaseToken. Changes to the
|
||||
// subscription always just result in a new purchaseToken
|
||||
final ProcessorCustomer pc = new ProcessorCustomer(purchaseToken, PaymentProvider.GOOGLE_PLAY_BILLING);
|
||||
|
||||
// For IAP providers, the subscriptionId and the customerId are both just the purchaseToken. Changes to the
|
||||
// subscription always just result in a new purchaseToken
|
||||
final ProcessorCustomer pc = new ProcessorCustomer(purchaseToken, PaymentProvider.GOOGLE_PLAY_BILLING);
|
||||
return getSubscriber(subscriberCredentials)
|
||||
|
||||
return googlePlayBillingManager
|
||||
// Validating ensures we don't allow a user-determined token that's totally bunk into the subscription manager,
|
||||
// but we don't want to acknowledge it until it's successfully persisted.
|
||||
.validateToken(purchaseToken)
|
||||
// Store the purchaseToken with the subscriber
|
||||
.thenCompose(validatedToken -> subscriptions.setIapPurchase(
|
||||
record, pc, purchaseToken, validatedToken.getLevel(), subscriberCredentials.now())
|
||||
// Now that the purchaseToken is durable, we can acknowledge it
|
||||
.thenCompose(ignore -> validatedToken.acknowledgePurchase())
|
||||
.thenApply(ignore -> validatedToken.getLevel()));
|
||||
});
|
||||
// Check the record for an existing subscription
|
||||
.thenCompose(record -> {
|
||||
if (record.processorCustomer != null
|
||||
&& record.processorCustomer.processor() != PaymentProvider.GOOGLE_PLAY_BILLING) {
|
||||
return CompletableFuture.failedFuture(
|
||||
new SubscriptionException.ProcessorConflict("existing processor does not match"));
|
||||
}
|
||||
|
||||
// If we're replacing an existing purchaseToken, cancel it first
|
||||
return Optional.ofNullable(record.processorCustomer)
|
||||
.map(ProcessorCustomer::customerId)
|
||||
.filter(existingToken -> !purchaseToken.equals(existingToken))
|
||||
.map(googlePlayBillingManager::cancelAllActiveSubscriptions)
|
||||
.orElseGet(() -> CompletableFuture.completedFuture(null))
|
||||
.thenApply(ignored -> record);
|
||||
})
|
||||
|
||||
// Validate and set the purchaseToken
|
||||
.thenCompose(record -> googlePlayBillingManager
|
||||
|
||||
// Validating ensures we don't allow a user-determined token that's totally bunk into the subscription manager,
|
||||
// but we don't want to acknowledge it until it's successfully persisted.
|
||||
.validateToken(purchaseToken)
|
||||
|
||||
// Store the purchaseToken with the subscriber
|
||||
.thenCompose(validatedToken -> subscriptions.setIapPurchase(
|
||||
record, pc, purchaseToken, validatedToken.getLevel(), subscriberCredentials.now())
|
||||
// Now that the purchaseToken is durable, we can acknowledge it
|
||||
.thenCompose(ignore -> validatedToken.acknowledgePurchase())
|
||||
.thenApply(ignore -> validatedToken.getLevel())));
|
||||
}
|
||||
|
||||
private SubscriptionPaymentProcessor getProcessor(PaymentProvider provider) {
|
||||
|
||||
Reference in New Issue
Block a user