mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-21 17:29:32 +01:00
Don't fail backup redemption pipeline after 24hrs.
This commit is contained in:
@@ -44,6 +44,7 @@ import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.InAppPaymentData
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.PendingOneTimeDonation
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.jobmanager.Job
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper
|
||||
@@ -223,10 +224,14 @@ object InAppPaymentsRepository {
|
||||
* Returns a duration to utilize for jobs tied to different payment methods. For long running bank transfers, we need to
|
||||
* allow extra time for completion.
|
||||
*/
|
||||
fun resolveContextJobLifespan(inAppPayment: InAppPaymentTable.InAppPayment): Duration {
|
||||
return when (inAppPayment.data.paymentMethodType) {
|
||||
InAppPaymentData.PaymentMethodType.SEPA_DEBIT, InAppPaymentData.PaymentMethodType.IDEAL -> 30.days
|
||||
else -> 1.days
|
||||
fun resolveContextJobLifespanMillis(inAppPayment: InAppPaymentTable.InAppPayment): Long {
|
||||
return if (inAppPayment.type == InAppPaymentType.RECURRING_BACKUP) {
|
||||
Job.Parameters.IMMORTAL
|
||||
} else {
|
||||
when (inAppPayment.data.paymentMethodType) {
|
||||
InAppPaymentData.PaymentMethodType.SEPA_DEBIT, InAppPaymentData.PaymentMethodType.IDEAL -> 30.days.inWholeMilliseconds
|
||||
else -> 1.days.inWholeMilliseconds
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.thoughtcrime.securesms.util.parcelers.MillisecondDurationParceler
|
||||
import org.thoughtcrime.securesms.util.parcelers.NullableSubscriberIdParceler
|
||||
import org.whispersystems.signalservice.api.subscriptions.SubscriberId
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.hours
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@@ -184,6 +185,20 @@ class InAppPaymentTable(context: Context, databaseHelper: SignalDatabase) : Data
|
||||
AppDependencies.databaseObserver.notifyInAppPaymentsObservers(inAppPayment)
|
||||
}
|
||||
|
||||
fun getOldPendingPayments(type: InAppPaymentType): List<InAppPayment> {
|
||||
val oneDayAgo = System.currentTimeMillis().milliseconds - 24.hours
|
||||
return readableDatabase
|
||||
.select()
|
||||
.from(TABLE_NAME)
|
||||
.where(
|
||||
"$STATE = ? AND $TYPE = ? AND $UPDATED_AT <= ${oneDayAgo.inWholeSeconds}",
|
||||
State.serialize(State.PENDING),
|
||||
InAppPaymentType.serialize(type)
|
||||
)
|
||||
.run()
|
||||
.readToList(mapper = InAppPayment::deserialize)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the user has submitted a pre-pending recurring donation.
|
||||
* In this state, the user would have had to cancel their subscription or be in the process of trying
|
||||
|
||||
@@ -174,7 +174,11 @@ class BackupSubscriptionCheckJob private constructor(parameters: Parameters) : C
|
||||
val isGooglePlayBillingCanceled = purchase is BillingPurchaseResult.Success && !purchase.isAutoRenewing
|
||||
|
||||
if (isGooglePlayBillingCanceled && (!hasActiveSignalSubscription || isSignalSubscriptionFailedOrCanceled)) {
|
||||
Log.i(TAG, "Valid cancel state. Clearing mismatch. (isGooglePlayBillingCanceled: true, hasActiveSignalSubscription: $hasActiveSignalSubscription, isSignalSubscriptionFailedOrCanceled: $isSignalSubscriptionFailedOrCanceled", true)
|
||||
Log.i(
|
||||
TAG,
|
||||
"Valid cancel state. Clearing mismatch. (isGooglePlayBillingCanceled: true, hasActiveSignalSubscription: $hasActiveSignalSubscription, isSignalSubscriptionFailedOrCanceled: $isSignalSubscriptionFailedOrCanceled",
|
||||
true
|
||||
)
|
||||
SignalStore.backup.subscriptionStateMismatchDetected = false
|
||||
return Result.success()
|
||||
} else {
|
||||
|
||||
@@ -151,6 +151,8 @@ class InAppPaymentKeepAliveJob private constructor(
|
||||
return
|
||||
}
|
||||
|
||||
clearOldPendingPaymentsIfRequired()
|
||||
|
||||
val activeInAppPayment = getActiveInAppPayment(subscriber, subscription)
|
||||
if (activeInAppPayment == null) {
|
||||
warn(type, "Failed to generate active in-app payment. Exiting")
|
||||
@@ -227,6 +229,38 @@ class InAppPaymentKeepAliveJob private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If we have a pending payment that is at least 24 hours old AND we have no jobs
|
||||
* enqueued that are associated with it, then something weird has happened. We should
|
||||
* fail the job and let the keep-alive check create a new one as necessary.
|
||||
*/
|
||||
private fun clearOldPendingPaymentsIfRequired() {
|
||||
val inAppPayments = SignalDatabase.inAppPayments.getOldPendingPayments(type.inAppPaymentType)
|
||||
|
||||
inAppPayments.forEach { inAppPayment ->
|
||||
val queue = InAppPaymentsRepository.resolveJobQueueKey(inAppPayment)
|
||||
|
||||
if (AppDependencies.jobManager.isQueueEmpty(queue)) {
|
||||
Log.i(TAG, "User has an aged-out pending in-app payment [${inAppPayment.id}][${inAppPayment.type}]. Marking failed and proceeding with check.")
|
||||
SignalDatabase.inAppPayments.update(
|
||||
inAppPayment = inAppPayment.copy(
|
||||
notified = true,
|
||||
endOfPeriod = 0.seconds,
|
||||
subscriberId = null,
|
||||
state = InAppPaymentTable.State.END,
|
||||
data = inAppPayment.data.newBuilder()
|
||||
.error(
|
||||
InAppPaymentData.Error(
|
||||
type = InAppPaymentData.Error.Type.REDEMPTION
|
||||
)
|
||||
)
|
||||
.build()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getActiveInAppPayment(
|
||||
subscriber: InAppPaymentSubscriberRecord,
|
||||
subscription: ActiveSubscription.Subscription
|
||||
|
||||
@@ -54,7 +54,7 @@ class InAppPaymentOneTimeContextJob private constructor(
|
||||
parameters = Parameters.Builder()
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setQueue(InAppPaymentsRepository.resolveJobQueueKey(inAppPayment))
|
||||
.setLifespan(InAppPaymentsRepository.resolveContextJobLifespan(inAppPayment).inWholeMilliseconds)
|
||||
.setLifespan(InAppPaymentsRepository.resolveContextJobLifespanMillis(inAppPayment))
|
||||
.setMaxAttempts(Parameters.UNLIMITED)
|
||||
.build()
|
||||
)
|
||||
|
||||
@@ -61,7 +61,7 @@ class InAppPaymentRecurringContextJob private constructor(
|
||||
parameters = Parameters.Builder()
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setQueue(InAppPaymentsRepository.resolveJobQueueKey(inAppPayment))
|
||||
.setLifespan(InAppPaymentsRepository.resolveContextJobLifespan(inAppPayment).inWholeMilliseconds)
|
||||
.setLifespan(InAppPaymentsRepository.resolveContextJobLifespanMillis(inAppPayment))
|
||||
.setMaxAttempts(Parameters.UNLIMITED)
|
||||
.build()
|
||||
)
|
||||
|
||||
@@ -44,7 +44,7 @@ abstract class InAppPaymentSetupJob(
|
||||
protected fun getParameters(inAppPayment: InAppPaymentTable.InAppPayment): Parameters {
|
||||
return Parameters.Builder()
|
||||
.setQueue(InAppPaymentsRepository.resolveJobQueueKey(inAppPayment))
|
||||
.setLifespan(InAppPaymentsRepository.resolveContextJobLifespan(inAppPayment).inWholeMilliseconds)
|
||||
.setLifespan(InAppPaymentsRepository.resolveContextJobLifespanMillis(inAppPayment))
|
||||
.setMaxAttempts(Parameters.UNLIMITED)
|
||||
.build()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user