Implement initial support for IAP data.

This commit is contained in:
Alex Hart
2024-12-19 16:34:29 -04:00
committed by Greyson Parrelli
parent f537fa6436
commit f2b4bd0585
35 changed files with 957 additions and 241 deletions

View File

@@ -165,9 +165,10 @@ public class ApplicationMigrations {
static final int EMOJI_SEARCH_INDEX_CHECK_2 = 121;
static final int QUOTE_AUTHOR_FIX = 122;
static final int BAD_E164_FIX = 123;
static final int GPB_TOKEN_MIGRATION = 124;
}
public static final int CURRENT_VERSION = 123;
public static final int CURRENT_VERSION = 124;
/**
* This *must* be called after the {@link JobManager} has been instantiated, but *before* the call
@@ -758,6 +759,10 @@ public class ApplicationMigrations {
jobs.put(Version.BAD_E164_FIX, new BadE164MigrationJob());
}
if (lastSeenVersion < Version.GPB_TOKEN_MIGRATION) {
jobs.put(Version.GPB_TOKEN_MIGRATION, new GooglePlayBillingPurchaseTokenMigrationJob());
}
return jobs;
}

View File

@@ -0,0 +1,98 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.migrations
import kotlinx.coroutines.runBlocking
import okio.IOException
import org.signal.core.util.billing.BillingPurchaseResult
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentsRepository
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.jobmanager.Job
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.storage.StorageSyncHelper
import org.whispersystems.signalservice.api.storage.IAPSubscriptionId
/**
* When we migrate subscriptions, purchase tokens are stored as '-' string. This migration
* goes in and updates that purchase token with the real value from the latest subscription, if
* available.
*/
internal class GooglePlayBillingPurchaseTokenMigrationJob private constructor(
parameters: Parameters
) : MigrationJob(parameters) {
constructor() : this(
Parameters.Builder()
.addConstraint(NetworkConstraint.KEY)
.build()
)
companion object {
private val TAG = Log.tag(GooglePlayBillingPurchaseTokenMigrationJob::class)
const val KEY = "GooglePlayBillingPurchaseTokenMigrationJob"
}
override fun getFactoryKey(): String = KEY
override fun isUiBlocking(): Boolean = false
override fun performMigration() {
if (!SignalStore.account.isRegistered) {
return
}
val backupSubscriber = InAppPaymentsRepository.getSubscriber(InAppPaymentSubscriberRecord.Type.BACKUP) ?: return
if (backupSubscriber.iapSubscriptionId?.purchaseToken == "-") {
val purchaseResult: BillingPurchaseResult.Success? = runBlocking {
if (AppDependencies.billingApi.isApiAvailable()) {
val purchase = AppDependencies.billingApi.queryPurchases()
if (purchase is BillingPurchaseResult.Success) {
Log.d(TAG, "Successfully found purchase result.")
purchase
} else {
Log.d(TAG, "No purchase was available.")
null
}
} else {
Log.d(TAG, "Billing API is not available.")
null
}
}
if (purchaseResult == null) {
return
}
InAppPaymentsRepository.setSubscriber(
backupSubscriber.copy(
iapSubscriptionId = IAPSubscriptionId.GooglePlayBillingPurchaseToken(purchaseToken = purchaseResult.purchaseToken)
)
)
SignalDatabase.recipients.markNeedsSync(Recipient.self().id)
StorageSyncHelper.scheduleSyncForDataChange()
}
}
override fun shouldRetry(e: Exception): Boolean {
Log.w(TAG, "Checking retry state for exception.", e)
return e is IOException
}
class Factory : Job.Factory<GooglePlayBillingPurchaseTokenMigrationJob> {
override fun create(parameters: Parameters, serializedData: ByteArray?): GooglePlayBillingPurchaseTokenMigrationJob {
return GooglePlayBillingPurchaseTokenMigrationJob(parameters)
}
}
}

View File

@@ -36,11 +36,12 @@ internal class SubscriberIdMigrationJob(
if (subscriber != null) {
SignalDatabase.inAppPaymentSubscribers.insertOrReplace(
InAppPaymentSubscriberRecord(
subscriber.subscriberId,
subscriber.currency,
InAppPaymentSubscriberRecord.Type.DONATION,
SignalStore.inAppPayments.shouldCancelSubscriptionBeforeNextSubscribeAttempt,
SignalStore.inAppPayments.getSubscriptionPaymentSourceType().toPaymentMethodType()
subscriberId = subscriber.subscriberId,
currency = subscriber.currency,
type = InAppPaymentSubscriberRecord.Type.DONATION,
requiresCancel = SignalStore.inAppPayments.shouldCancelSubscriptionBeforeNextSubscribeAttempt,
paymentMethodType = SignalStore.inAppPayments.getSubscriptionPaymentSourceType().toPaymentMethodType(),
iapSubscriptionId = null
)
)
}