Fix crash loop when writing invalid currency .
This commit is contained in:
parent
71979b34db
commit
cb171092cf
16 changed files with 63 additions and 40 deletions
|
@ -47,6 +47,7 @@ import org.whispersystems.signalservice.api.push.ServiceId.ACI
|
|||
import org.whispersystems.signalservice.api.push.ServiceId.PNI
|
||||
import org.whispersystems.signalservice.api.subscriptions.SubscriberId
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.util.Currency
|
||||
import java.util.UUID
|
||||
import kotlin.random.Random
|
||||
|
||||
|
@ -253,7 +254,7 @@ class BackupTest {
|
|||
SignalDatabase.recipients.setProfileName(self.id, ProfileName.fromParts("Peter", "Parker"))
|
||||
SignalDatabase.recipients.setProfileAvatar(self.id, "https://example.com/")
|
||||
|
||||
InAppPaymentsRepository.setSubscriber(InAppPaymentSubscriberRecord(SubscriberId.generate(), "USD", InAppPaymentSubscriberRecord.Type.DONATION, false, InAppPaymentData.PaymentMethodType.UNKNOWN))
|
||||
InAppPaymentsRepository.setSubscriber(InAppPaymentSubscriberRecord(SubscriberId.generate(), Currency.getInstance("USD"), InAppPaymentSubscriberRecord.Type.DONATION, false, InAppPaymentData.PaymentMethodType.UNKNOWN))
|
||||
SignalStore.donationsValues().setDisplayBadgesOnProfile(false)
|
||||
|
||||
SignalStore.phoneNumberPrivacy().phoneNumberDiscoverabilityMode = PhoneNumberPrivacyValues.PhoneNumberDiscoverabilityMode.NOT_DISCOVERABLE
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore
|
|||
import org.thoughtcrime.securesms.testing.assertIs
|
||||
import org.thoughtcrime.securesms.testing.assertIsNotNull
|
||||
import org.whispersystems.signalservice.api.subscriptions.SubscriberId
|
||||
import java.util.Currency
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class SubscriberIdMigrationJobTest {
|
||||
|
@ -35,7 +36,7 @@ class SubscriberIdMigrationJobTest {
|
|||
@Test
|
||||
fun givenUSDSubscriber_whenIRunSubscriberIdMigrationJob_thenIExpectASingleEntry() {
|
||||
val subscriberId = SubscriberId.generate()
|
||||
SignalStore.donationsValues().setSubscriberCurrency("USD", InAppPaymentSubscriberRecord.Type.DONATION)
|
||||
SignalStore.donationsValues().setSubscriberCurrency(Currency.getInstance("USD"), InAppPaymentSubscriberRecord.Type.DONATION)
|
||||
SignalStore.donationsValues().setSubscriber("USD", subscriberId)
|
||||
SignalStore.donationsValues().setSubscriptionPaymentSourceType(PaymentSourceType.PayPal)
|
||||
SignalStore.donationsValues().shouldCancelSubscriptionBeforeNextSubscribeAttempt = true
|
||||
|
@ -48,7 +49,7 @@ class SubscriberIdMigrationJobTest {
|
|||
actual!!.subscriberId.bytes assertIs subscriberId.bytes
|
||||
actual.paymentMethodType assertIs InAppPaymentData.PaymentMethodType.PAYPAL
|
||||
actual.requiresCancel assertIs true
|
||||
actual.currencyCode assertIs "USD"
|
||||
actual.currency assertIs Currency.getInstance("USD")
|
||||
actual.type assertIs InAppPaymentSubscriberRecord.Type.DONATION
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.whispersystems.signalservice.api.push.UsernameLinkComponents
|
|||
import org.whispersystems.signalservice.api.storage.StorageRecordProtoUtil.defaultAccountRecord
|
||||
import org.whispersystems.signalservice.api.subscriptions.SubscriberId
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil
|
||||
import java.util.Currency
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
|
||||
object AccountDataProcessor {
|
||||
|
@ -51,7 +52,7 @@ object AccountDataProcessor {
|
|||
subscriptionManuallyCancelled = InAppPaymentsRepository.isUserManuallyCancelled(InAppPaymentSubscriberRecord.Type.DONATION),
|
||||
username = self.username.getOrNull(),
|
||||
subscriberId = subscriber?.subscriberId?.bytes?.toByteString() ?: defaultAccountRecord.subscriberId,
|
||||
subscriberCurrencyCode = subscriber?.currencyCode ?: defaultAccountRecord.subscriberCurrencyCode,
|
||||
subscriberCurrencyCode = subscriber?.currency?.currencyCode ?: defaultAccountRecord.subscriberCurrencyCode,
|
||||
accountSettings = AccountData.AccountSettings(
|
||||
storyViewReceiptsEnabled = SignalStore.storyValues().viewedReceiptsEnabled,
|
||||
typingIndicators = TextSecurePreferences.isTypingIndicatorsEnabled(context),
|
||||
|
@ -108,7 +109,7 @@ object AccountDataProcessor {
|
|||
|
||||
val subscriber = InAppPaymentSubscriberRecord(
|
||||
remoteSubscriberId,
|
||||
accountData.subscriberCurrencyCode,
|
||||
Currency.getInstance(accountData.subscriberCurrencyCode),
|
||||
InAppPaymentSubscriberRecord.Type.DONATION,
|
||||
localSubscriber?.requiresCancel ?: false,
|
||||
InAppPaymentsRepository.getLatestPaymentMethodType(InAppPaymentSubscriberRecord.Type.DONATION)
|
||||
|
|
|
@ -115,7 +115,7 @@ class RecurringInAppPaymentRepository(private val donationsService: DonationsSer
|
|||
InAppPaymentsRepository.setSubscriber(
|
||||
InAppPaymentSubscriberRecord(
|
||||
subscriberId = subscriberId,
|
||||
currencyCode = SignalStore.donationsValues().getSubscriptionCurrency(subscriberType).currencyCode,
|
||||
currency = SignalStore.donationsValues().getSubscriptionCurrency(subscriberType),
|
||||
type = subscriberType,
|
||||
requiresCancel = false,
|
||||
paymentMethodType = InAppPaymentData.PaymentMethodType.UNKNOWN
|
||||
|
@ -193,7 +193,7 @@ class RecurringInAppPaymentRepository(private val donationsService: DonationsSer
|
|||
AppDependencies.donationsService.updateSubscriptionLevel(
|
||||
subscriber.subscriberId,
|
||||
subscriptionLevel,
|
||||
subscriber.currencyCode,
|
||||
subscriber.currency.currencyCode,
|
||||
levelUpdateOperation.idempotencyKey.serialize(),
|
||||
subscriberType
|
||||
)
|
||||
|
|
|
@ -50,7 +50,7 @@ class SetCurrencyViewModel(
|
|||
InAppPaymentsRepository.setSubscriber(
|
||||
InAppPaymentSubscriberRecord(
|
||||
subscriberId = SubscriberId.generate(),
|
||||
currencyCode = currency.currencyCode,
|
||||
currency = currency,
|
||||
type = inAppPaymentType.requireSubscriberType(),
|
||||
requiresCancel = false,
|
||||
paymentMethodType = InAppPaymentData.PaymentMethodType.UNKNOWN
|
||||
|
|
|
@ -392,7 +392,7 @@ class DonateToSignalViewModel(
|
|||
if (selectedCurrency !in priceCurrencies) {
|
||||
Log.w(TAG, "Unsupported currency selection. Defaulting to USD. $selectedCurrency isn't supported.")
|
||||
val usd = PlatformCurrencyUtil.USD
|
||||
val newSubscriber = InAppPaymentsRepository.getSubscriber(usd, InAppPaymentSubscriberRecord.Type.DONATION) ?: InAppPaymentSubscriberRecord(SubscriberId.generate(), usd.currencyCode, InAppPaymentSubscriberRecord.Type.DONATION, false, InAppPaymentData.PaymentMethodType.UNKNOWN)
|
||||
val newSubscriber = InAppPaymentsRepository.getSubscriber(usd, InAppPaymentSubscriberRecord.Type.DONATION) ?: InAppPaymentSubscriberRecord(SubscriberId.generate(), usd, InAppPaymentSubscriberRecord.Type.DONATION, false, InAppPaymentData.PaymentMethodType.UNKNOWN)
|
||||
InAppPaymentsRepository.setSubscriber(newSubscriber)
|
||||
subscriptionsRepository.syncAccountRecord().subscribe()
|
||||
}
|
||||
|
|
|
@ -240,7 +240,7 @@ class InternalConversationSettingsFragment : DSLSettingsFragment(
|
|||
// TODO [alex] - DB on main thread!
|
||||
val subscriber: InAppPaymentSubscriberRecord? = InAppPaymentsRepository.getSubscriber(InAppPaymentSubscriberRecord.Type.DONATION)
|
||||
val summary = if (subscriber != null) {
|
||||
"""currency code: ${subscriber.currencyCode}
|
||||
"""currency code: ${subscriber.currency.currencyCode}
|
||||
|subscriber id: ${subscriber.subscriberId.serialize()}
|
||||
""".trimMargin()
|
||||
} else {
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord
|
|||
import org.thoughtcrime.securesms.database.model.databaseprotos.InAppPaymentData
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.whispersystems.signalservice.api.subscriptions.SubscriberId
|
||||
import java.util.Currency
|
||||
|
||||
/**
|
||||
* A table matching up SubscriptionIds to currency codes and type
|
||||
|
@ -77,7 +78,7 @@ class InAppPaymentSubscriberTable(
|
|||
* This is a destructive, mutating operation. For setting specific values, prefer the alternative setters available on this table class.
|
||||
*/
|
||||
fun insertOrReplace(inAppPaymentSubscriberRecord: InAppPaymentSubscriberRecord) {
|
||||
Log.i(TAG, "Setting subscriber for currency ${inAppPaymentSubscriberRecord.currencyCode}", Exception(), true)
|
||||
Log.i(TAG, "Setting subscriber for currency ${inAppPaymentSubscriberRecord.currency.currencyCode}", Exception(), true)
|
||||
|
||||
writableDatabase.withinTransaction { db ->
|
||||
db.insertInto(TABLE_NAME)
|
||||
|
@ -85,7 +86,7 @@ class InAppPaymentSubscriberTable(
|
|||
.run(conflictStrategy = SQLiteDatabase.CONFLICT_REPLACE)
|
||||
|
||||
SignalStore.donationsValues().setSubscriberCurrency(
|
||||
inAppPaymentSubscriberRecord.currencyCode,
|
||||
inAppPaymentSubscriberRecord.currency,
|
||||
inAppPaymentSubscriberRecord.type
|
||||
)
|
||||
}
|
||||
|
@ -137,7 +138,7 @@ class InAppPaymentSubscriberTable(
|
|||
override fun serialize(data: InAppPaymentSubscriberRecord): ContentValues {
|
||||
return contentValuesOf(
|
||||
SUBSCRIBER_ID to data.subscriberId.serialize(),
|
||||
CURRENCY_CODE to data.currencyCode.uppercase(),
|
||||
CURRENCY_CODE to data.currency.currencyCode.uppercase(),
|
||||
TYPE to TypeSerializer.serialize(data.type),
|
||||
REQUIRES_CANCEL to data.requiresCancel,
|
||||
PAYMENT_METHOD_TYPE to data.paymentMethodType.value
|
||||
|
@ -145,11 +146,13 @@ class InAppPaymentSubscriberTable(
|
|||
}
|
||||
|
||||
override fun deserialize(input: Cursor): InAppPaymentSubscriberRecord {
|
||||
val type = TypeSerializer.deserialize(input.requireInt(TYPE))
|
||||
val currencyCode = input.requireNonNullString(CURRENCY_CODE).takeIf { it.isNotEmpty() }
|
||||
return InAppPaymentSubscriberRecord(
|
||||
subscriberId = SubscriberId.deserialize(input.requireNonNullString(SUBSCRIBER_ID)),
|
||||
currencyCode = input.requireNonNullString(CURRENCY_CODE),
|
||||
type = TypeSerializer.deserialize(input.requireInt(TYPE)),
|
||||
requiresCancel = input.requireBoolean(REQUIRES_CANCEL),
|
||||
currency = currencyCode?.let { Currency.getInstance(it) } ?: SignalStore.donationsValues().getSubscriptionCurrency(type),
|
||||
type = type,
|
||||
requiresCancel = input.requireBoolean(REQUIRES_CANCEL) || currencyCode.isNullOrBlank(),
|
||||
paymentMethodType = InAppPaymentData.PaymentMethodType.fromValue(input.requireInt(PAYMENT_METHOD_TYPE)) ?: InAppPaymentData.PaymentMethodType.UNKNOWN
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.thoughtcrime.securesms.database.model
|
|||
import org.thoughtcrime.securesms.database.InAppPaymentTable
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.InAppPaymentData
|
||||
import org.whispersystems.signalservice.api.subscriptions.SubscriberId
|
||||
import java.util.Currency
|
||||
|
||||
/**
|
||||
* Represents a SubscriberId and metadata that can be used for a recurring
|
||||
|
@ -15,7 +16,7 @@ import org.whispersystems.signalservice.api.subscriptions.SubscriberId
|
|||
*/
|
||||
data class InAppPaymentSubscriberRecord(
|
||||
val subscriberId: SubscriberId,
|
||||
val currencyCode: String,
|
||||
val currency: Currency,
|
||||
val type: Type,
|
||||
val requiresCancel: Boolean,
|
||||
val paymentMethodType: InAppPaymentData.PaymentMethodType
|
||||
|
|
|
@ -164,7 +164,7 @@ class ExternalLaunchDonationJob private constructor(
|
|||
val updateSubscriptionLevelResponse = AppDependencies.donationsService.updateSubscriptionLevel(
|
||||
subscriber.subscriberId,
|
||||
subscriptionLevel,
|
||||
subscriber.currencyCode,
|
||||
subscriber.currency.currencyCode,
|
||||
levelUpdateOperation.idempotencyKey.serialize(),
|
||||
subscriber.type
|
||||
)
|
||||
|
|
|
@ -233,7 +233,7 @@ class InAppPaymentAuthCheckJob private constructor(parameters: Parameters) : Bas
|
|||
val updateLevelResponse = AppDependencies.donationsService.updateSubscriptionLevel(
|
||||
subscriber.subscriberId,
|
||||
level,
|
||||
subscriber.currencyCode,
|
||||
subscriber.currency.currencyCode,
|
||||
updateOperation.idempotencyKey.serialize(),
|
||||
subscriber.type
|
||||
)
|
||||
|
|
|
@ -250,7 +250,7 @@ class InAppPaymentKeepAliveJob private constructor(
|
|||
inAppPaymentData = InAppPaymentData(
|
||||
badge = badge,
|
||||
amount = FiatValue(
|
||||
currencyCode = subscriber.currencyCode,
|
||||
currencyCode = subscriber.currency.currencyCode,
|
||||
amount = subscription.amount.toDecimalValue()
|
||||
),
|
||||
error = null,
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription.Cha
|
|||
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription.Subscription
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse
|
||||
import java.io.IOException
|
||||
import java.util.Currency
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
@ -288,10 +289,15 @@ class InAppPaymentRecurringContextJob private constructor(
|
|||
}
|
||||
|
||||
private fun handlePaymentFailure(inAppPayment: InAppPaymentTable.InAppPayment, subscription: Subscription, chargeFailure: ChargeFailure?) {
|
||||
val subscriber = SignalDatabase.inAppPaymentSubscribers.getBySubscriberId(inAppPayment.subscriberId!!)
|
||||
if (subscriber != null) {
|
||||
InAppPaymentsRepository.setShouldCancelSubscriptionBeforeNextSubscribeAttempt(subscriber, true)
|
||||
}
|
||||
SignalDatabase.inAppPaymentSubscribers.insertOrReplace(
|
||||
InAppPaymentSubscriberRecord(
|
||||
subscriberId = inAppPayment.subscriberId!!,
|
||||
currency = Currency.getInstance(inAppPayment.data.amount!!.currencyCode),
|
||||
type = inAppPayment.type.requireSubscriberType(),
|
||||
requiresCancel = true,
|
||||
paymentMethodType = inAppPayment.data.paymentMethodType
|
||||
)
|
||||
)
|
||||
|
||||
if (inAppPayment.data.redemption?.keepAlive == true) {
|
||||
info("Cancellation occurred during keep-alive. Setting cancellation state.")
|
||||
|
|
|
@ -245,7 +245,7 @@ internal class DonationsValues internal constructor(store: KeyValueStore) : Sign
|
|||
} else {
|
||||
InAppPaymentSubscriberRecord(
|
||||
SubscriberId.fromBytes(subscriberIdBytes),
|
||||
currencyCode,
|
||||
currency,
|
||||
InAppPaymentSubscriberRecord.Type.DONATION,
|
||||
shouldCancelSubscriptionBeforeNextSubscribeAttempt,
|
||||
getSubscriptionPaymentSourceType().toPaymentMethodType()
|
||||
|
@ -253,19 +253,19 @@ internal class DonationsValues internal constructor(store: KeyValueStore) : Sign
|
|||
}
|
||||
}
|
||||
|
||||
fun setSubscriberCurrency(currencyCode: String, type: InAppPaymentSubscriberRecord.Type) {
|
||||
fun setSubscriberCurrency(currency: Currency, type: InAppPaymentSubscriberRecord.Type) {
|
||||
if (type == InAppPaymentSubscriberRecord.Type.DONATION) {
|
||||
store.beginWrite()
|
||||
.putString(KEY_DONATION_SUBSCRIPTION_CURRENCY_CODE, currencyCode)
|
||||
.putString(KEY_DONATION_SUBSCRIPTION_CURRENCY_CODE, currency.currencyCode)
|
||||
.apply()
|
||||
|
||||
recurringDonationCurrencyPublisher.onNext(Currency.getInstance(currencyCode))
|
||||
recurringDonationCurrencyPublisher.onNext(currency)
|
||||
} else {
|
||||
store.beginWrite()
|
||||
.putString(KEY_BACKUPS_SUBSCRIPTION_CURRENCY_CODE, currencyCode)
|
||||
.putString(KEY_BACKUPS_SUBSCRIPTION_CURRENCY_CODE, currency.currencyCode)
|
||||
.apply()
|
||||
|
||||
recurringBackupCurrencyPublisher.onNext(Currency.getInstance(currencyCode))
|
||||
recurringBackupCurrencyPublisher.onNext(currency)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ internal class SubscriberIdMigrationJob(
|
|||
SignalDatabase.inAppPaymentSubscribers.insertOrReplace(
|
||||
InAppPaymentSubscriberRecord(
|
||||
subscriber.subscriberId,
|
||||
subscriber.currencyCode,
|
||||
subscriber.currency,
|
||||
InAppPaymentSubscriberRecord.Type.DONATION,
|
||||
SignalStore.donationsValues().shouldCancelSubscriptionBeforeNextSubscribeAttempt,
|
||||
SignalStore.donationsValues().getSubscriptionPaymentSourceType().toPaymentMethodType()
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.thoughtcrime.securesms.database.model.RecipientRecord;
|
|||
import org.thoughtcrime.securesms.database.model.databaseprotos.InAppPaymentData;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.api.storage.SignalAccountRecord;
|
||||
|
@ -32,6 +33,7 @@ import org.whispersystems.signalservice.internal.storage.protos.AccountRecord;
|
|||
import org.whispersystems.signalservice.internal.storage.protos.ContactRecord.IdentityState;
|
||||
import org.whispersystems.signalservice.internal.storage.protos.GroupV2Record;
|
||||
|
||||
import java.util.Currency;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -281,24 +283,32 @@ public final class StorageSyncModels {
|
|||
if (subscriber == null) {
|
||||
return new SignalAccountRecord.Subscriber(null, null);
|
||||
} else {
|
||||
return new SignalAccountRecord.Subscriber(subscriber.getCurrencyCode(), subscriber.getSubscriberId().getBytes());
|
||||
return new SignalAccountRecord.Subscriber(subscriber.getCurrency().getCurrencyCode(), subscriber.getSubscriberId().getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO - We need to store the subscriber type.
|
||||
*/
|
||||
public static @Nullable InAppPaymentSubscriberRecord remoteToLocalSubscriber(
|
||||
@NonNull SignalAccountRecord.Subscriber subscriber,
|
||||
@NonNull InAppPaymentSubscriberRecord.Type type
|
||||
) {
|
||||
if (subscriber.getId().isPresent()) {
|
||||
SubscriberId subscriberId = SubscriberId.fromBytes(subscriber.getId().get());
|
||||
InAppPaymentSubscriberRecord localSubscriberRecord = SignalDatabase.inAppPaymentSubscribers().getBySubscriberId(subscriberId);
|
||||
boolean requiresCancel = localSubscriberRecord != null && localSubscriberRecord.getRequiresCancel();
|
||||
InAppPaymentData.PaymentMethodType paymentMethodType = localSubscriberRecord != null ? localSubscriberRecord.getPaymentMethodType() : InAppPaymentData.PaymentMethodType.UNKNOWN;
|
||||
SubscriberId subscriberId = SubscriberId.fromBytes(subscriber.getId().get());
|
||||
InAppPaymentSubscriberRecord localSubscriberRecord = SignalDatabase.inAppPaymentSubscribers().getBySubscriberId(subscriberId);
|
||||
boolean requiresCancel = localSubscriberRecord != null && localSubscriberRecord.getRequiresCancel();
|
||||
InAppPaymentData.PaymentMethodType paymentMethodType = localSubscriberRecord != null ? localSubscriberRecord.getPaymentMethodType() : InAppPaymentData.PaymentMethodType.UNKNOWN;
|
||||
|
||||
return new InAppPaymentSubscriberRecord(subscriberId, subscriber.getCurrencyCode().get(), type, requiresCancel, paymentMethodType);
|
||||
Currency currency;
|
||||
if (subscriber.getCurrencyCode().isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
try {
|
||||
currency = Currency.getInstance(subscriber.getCurrencyCode().get());
|
||||
} catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return new InAppPaymentSubscriberRecord(subscriberId, currency, type, requiresCancel, paymentMethodType);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue