kopia lustrzana https://github.com/ryukoposting/Signal-Android
Fix retry issue with payment processing.
rodzic
cf9b91ebd4
commit
b0788f7307
|
@ -6,6 +6,7 @@ import com.google.android.gms.wallet.PaymentData
|
||||||
import io.reactivex.rxjava3.core.Completable
|
import io.reactivex.rxjava3.core.Completable
|
||||||
import io.reactivex.rxjava3.core.Single
|
import io.reactivex.rxjava3.core.Single
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
|
import org.signal.core.util.logging.Log
|
||||||
import org.signal.core.util.money.FiatMoney
|
import org.signal.core.util.money.FiatMoney
|
||||||
import org.signal.donations.GooglePayApi
|
import org.signal.donations.GooglePayApi
|
||||||
import org.signal.donations.GooglePayPaymentSource
|
import org.signal.donations.GooglePayPaymentSource
|
||||||
|
@ -15,6 +16,7 @@ import org.thoughtcrime.securesms.jobs.BoostReceiptRequestResponseJob
|
||||||
import org.thoughtcrime.securesms.jobs.SubscriptionReceiptRequestResponseJob
|
import org.thoughtcrime.securesms.jobs.SubscriptionReceiptRequestResponseJob
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper
|
import org.thoughtcrime.securesms.storage.StorageSyncHelper
|
||||||
|
import org.thoughtcrime.securesms.subscription.LevelUpdate
|
||||||
import org.thoughtcrime.securesms.subscription.LevelUpdateOperation
|
import org.thoughtcrime.securesms.subscription.LevelUpdateOperation
|
||||||
import org.thoughtcrime.securesms.subscription.Subscriber
|
import org.thoughtcrime.securesms.subscription.Subscriber
|
||||||
import org.thoughtcrime.securesms.util.Environment
|
import org.thoughtcrime.securesms.util.Environment
|
||||||
|
@ -142,7 +144,8 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet
|
||||||
levelUpdateOperation.idempotencyKey.serialize()
|
levelUpdateOperation.idempotencyKey.serialize()
|
||||||
).flatMap(ServiceResponse<EmptyResponse>::flattenResult).ignoreElement().andThen {
|
).flatMap(ServiceResponse<EmptyResponse>::flattenResult).ignoreElement().andThen {
|
||||||
SignalStore.donationsValues().clearUserManuallyCancelled()
|
SignalStore.donationsValues().clearUserManuallyCancelled()
|
||||||
SignalStore.donationsValues().clearLevelOperation(levelUpdateOperation)
|
SignalStore.donationsValues().clearLevelOperation()
|
||||||
|
LevelUpdate.updateProcessingState(false)
|
||||||
it.onComplete()
|
it.onComplete()
|
||||||
}.andThen {
|
}.andThen {
|
||||||
val jobId = SubscriptionReceiptRequestResponseJob.enqueueSubscriptionContinuation()
|
val jobId = SubscriptionReceiptRequestResponseJob.enqueueSubscriptionContinuation()
|
||||||
|
@ -164,6 +167,8 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet
|
||||||
it.onError(DonationExceptions.TimedOutWaitingForTokenRedemption)
|
it.onError(DonationExceptions.TimedOutWaitingForTokenRedemption)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}.doOnError {
|
||||||
|
LevelUpdate.updateProcessingState(false)
|
||||||
}.subscribeOn(Schedulers.io())
|
}.subscribeOn(Schedulers.io())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,8 +181,10 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet
|
||||||
)
|
)
|
||||||
|
|
||||||
SignalStore.donationsValues().setLevelOperation(newOperation)
|
SignalStore.donationsValues().setLevelOperation(newOperation)
|
||||||
|
LevelUpdate.updateProcessingState(true)
|
||||||
newOperation
|
newOperation
|
||||||
} else {
|
} else {
|
||||||
|
LevelUpdate.updateProcessingState(true)
|
||||||
levelUpdateOperation
|
levelUpdateOperation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -206,4 +213,8 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet
|
||||||
ApplicationDependencies.getDonationsService().setDefaultPaymentMethodId(it.subscriberId, paymentMethodId)
|
ApplicationDependencies.getDonationsService().setDefaultPaymentMethodId(it.subscriberId, paymentMethodId)
|
||||||
}.flatMap(ServiceResponse<EmptyResponse>::flattenResult).ignoreElement()
|
}.flatMap(ServiceResponse<EmptyResponse>::flattenResult).ignoreElement()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TAG = Log.tag(DonationPaymentRepository::class.java)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -193,6 +193,7 @@ class BoostFragment : DSLSettingsBottomSheetFragment(
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
findNavController().popBackStack()
|
findNavController().popBackStack()
|
||||||
}
|
}
|
||||||
|
.show()
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "Error occurred while processing payment", throwable)
|
Log.w(TAG, "Error occurred while processing payment", throwable)
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
|
@ -202,6 +203,7 @@ class BoostFragment : DSLSettingsBottomSheetFragment(
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
findNavController().popBackStack()
|
findNavController().popBackStack()
|
||||||
}
|
}
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,6 +216,7 @@ class BoostFragment : DSLSettingsBottomSheetFragment(
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
findNavController().popBackStack()
|
findNavController().popBackStack()
|
||||||
}
|
}
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startAnimationAboveSelectedBoost(view: View) {
|
private fun startAnimationAboveSelectedBoost(view: View) {
|
||||||
|
|
|
@ -14,9 +14,8 @@ import org.signal.core.util.logging.Log
|
||||||
import org.thoughtcrime.securesms.components.settings.app.subscription.SubscriptionsRepository
|
import org.thoughtcrime.securesms.components.settings.app.subscription.SubscriptionsRepository
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.subscription.LevelUpdateOperation
|
import org.thoughtcrime.securesms.subscription.LevelUpdate
|
||||||
import org.thoughtcrime.securesms.util.livedata.Store
|
import org.thoughtcrime.securesms.util.livedata.Store
|
||||||
import org.whispersystems.libsignal.util.guava.Optional
|
|
||||||
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
|
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
|
||||||
|
|
||||||
class ManageDonationsViewModel(
|
class ManageDonationsViewModel(
|
||||||
|
@ -43,11 +42,11 @@ class ManageDonationsViewModel(
|
||||||
fun refresh() {
|
fun refresh() {
|
||||||
disposables.clear()
|
disposables.clear()
|
||||||
|
|
||||||
val levelUpdateOperationEdges: Observable<Optional<LevelUpdateOperation>> = SignalStore.donationsValues().levelUpdateOperationObservable.distinctUntilChanged()
|
val levelUpdateOperationEdges: Observable<Boolean> = LevelUpdate.isProcessing.distinctUntilChanged()
|
||||||
val activeSubscription: Single<ActiveSubscription> = subscriptionsRepository.getActiveSubscription()
|
val activeSubscription: Single<ActiveSubscription> = subscriptionsRepository.getActiveSubscription()
|
||||||
|
|
||||||
disposables += levelUpdateOperationEdges.flatMapSingle { optionalKey ->
|
disposables += levelUpdateOperationEdges.flatMapSingle { isProcessing ->
|
||||||
if (optionalKey.isPresent) {
|
if (isProcessing) {
|
||||||
Single.just(ManageDonationsState.TransactionState.InTransaction)
|
Single.just(ManageDonationsState.TransactionState.InTransaction)
|
||||||
} else {
|
} else {
|
||||||
activeSubscription.map { ManageDonationsState.TransactionState.NotInTransaction(it) }
|
activeSubscription.map { ManageDonationsState.TransactionState.NotInTransaction(it) }
|
||||||
|
|
|
@ -241,6 +241,7 @@ class SubscribeFragment : DSLSettingsFragment(
|
||||||
requireActivity().finish()
|
requireActivity().finish()
|
||||||
requireActivity().startActivity(AppSettingsActivity.subscriptions(requireContext()))
|
requireActivity().startActivity(AppSettingsActivity.subscriptions(requireContext()))
|
||||||
}
|
}
|
||||||
|
.show()
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "Error occurred while processing payment", throwable)
|
Log.w(TAG, "Error occurred while processing payment", throwable)
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
|
@ -249,6 +250,7 @@ class SubscribeFragment : DSLSettingsFragment(
|
||||||
.setPositiveButton(android.R.string.ok) { dialog, _ ->
|
.setPositiveButton(android.R.string.ok) { dialog, _ ->
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
}
|
}
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,6 +283,7 @@ class SubscribeFragment : DSLSettingsFragment(
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
findNavController().popBackStack()
|
findNavController().popBackStack()
|
||||||
}
|
}
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.DonationP
|
||||||
import org.thoughtcrime.securesms.components.settings.app.subscription.SubscriptionsRepository
|
import org.thoughtcrime.securesms.components.settings.app.subscription.SubscriptionsRepository
|
||||||
import org.thoughtcrime.securesms.components.settings.app.subscription.models.CurrencySelection
|
import org.thoughtcrime.securesms.components.settings.app.subscription.models.CurrencySelection
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
|
import org.thoughtcrime.securesms.subscription.LevelUpdate
|
||||||
import org.thoughtcrime.securesms.subscription.Subscription
|
import org.thoughtcrime.securesms.subscription.Subscription
|
||||||
import org.thoughtcrime.securesms.util.livedata.Store
|
import org.thoughtcrime.securesms.util.livedata.Store
|
||||||
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
|
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
|
||||||
|
@ -51,10 +52,10 @@ class SubscribeViewModel(
|
||||||
val allSubscriptions: Observable<List<Subscription>> = currency.switchMapSingle { subscriptionsRepository.getSubscriptions(it) }
|
val allSubscriptions: Observable<List<Subscription>> = currency.switchMapSingle { subscriptionsRepository.getSubscriptions(it) }
|
||||||
refreshActiveSubscription()
|
refreshActiveSubscription()
|
||||||
|
|
||||||
disposables += SignalStore.donationsValues().levelUpdateOperationObservable.subscribeBy {
|
disposables += LevelUpdate.isProcessing.subscribeBy {
|
||||||
store.update { state ->
|
store.update { state ->
|
||||||
state.copy(
|
state.copy(
|
||||||
hasInProgressSubscriptionTransaction = it.isPresent
|
hasInProgressSubscriptionTransaction = it
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,6 +114,7 @@ class SubscribeViewModel(
|
||||||
onComplete = {
|
onComplete = {
|
||||||
eventPublisher.onNext(DonationEvent.SubscriptionCancelled)
|
eventPublisher.onNext(DonationEvent.SubscriptionCancelled)
|
||||||
SignalStore.donationsValues().setLastEndOfPeriod(0L)
|
SignalStore.donationsValues().setLastEndOfPeriod(0L)
|
||||||
|
SignalStore.donationsValues().clearLevelOperation()
|
||||||
SignalStore.donationsValues().markUserManuallyCancelled()
|
SignalStore.donationsValues().markUserManuallyCancelled()
|
||||||
refreshActiveSubscription()
|
refreshActiveSubscription()
|
||||||
store.update { it.copy(stage = SubscribeState.Stage.READY) }
|
store.update { it.copy(stage = SubscribeState.Stage.READY) }
|
||||||
|
|
|
@ -12,7 +12,6 @@ import org.thoughtcrime.securesms.payments.currency.CurrencyUtil
|
||||||
import org.thoughtcrime.securesms.subscription.LevelUpdateOperation
|
import org.thoughtcrime.securesms.subscription.LevelUpdateOperation
|
||||||
import org.thoughtcrime.securesms.subscription.Subscriber
|
import org.thoughtcrime.securesms.subscription.Subscriber
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
import org.whispersystems.libsignal.util.guava.Optional
|
|
||||||
import org.whispersystems.signalservice.api.subscriptions.IdempotencyKey
|
import org.whispersystems.signalservice.api.subscriptions.IdempotencyKey
|
||||||
import org.whispersystems.signalservice.api.subscriptions.SubscriberId
|
import org.whispersystems.signalservice.api.subscriptions.SubscriberId
|
||||||
import java.util.Currency
|
import java.util.Currency
|
||||||
|
@ -45,9 +44,6 @@ internal class DonationsValues internal constructor(store: KeyValueStore) : Sign
|
||||||
private val boostCurrencyPublisher: Subject<Currency> by lazy { BehaviorSubject.createDefault(getBoostCurrency()) }
|
private val boostCurrencyPublisher: Subject<Currency> by lazy { BehaviorSubject.createDefault(getBoostCurrency()) }
|
||||||
val observableBoostCurrency: Observable<Currency> by lazy { boostCurrencyPublisher }
|
val observableBoostCurrency: Observable<Currency> by lazy { boostCurrencyPublisher }
|
||||||
|
|
||||||
private val levelUpdateOperationPublisher: Subject<Optional<LevelUpdateOperation>> by lazy { BehaviorSubject.createDefault(Optional.fromNullable(getLevelOperation())) }
|
|
||||||
val levelUpdateOperationObservable: Observable<Optional<LevelUpdateOperation>> by lazy { levelUpdateOperationPublisher }
|
|
||||||
|
|
||||||
fun getSubscriptionCurrency(): Currency {
|
fun getSubscriptionCurrency(): Currency {
|
||||||
val currencyCode = getString(KEY_SUBSCRIPTION_CURRENCY_CODE, null)
|
val currencyCode = getString(KEY_SUBSCRIPTION_CURRENCY_CODE, null)
|
||||||
val currency: Currency? = if (currencyCode == null) {
|
val currency: Currency? = if (currencyCode == null) {
|
||||||
|
@ -120,7 +116,7 @@ internal class DonationsValues internal constructor(store: KeyValueStore) : Sign
|
||||||
|
|
||||||
fun getLevelOperation(): LevelUpdateOperation? {
|
fun getLevelOperation(): LevelUpdateOperation? {
|
||||||
val level = getString(KEY_LEVEL, null)
|
val level = getString(KEY_LEVEL, null)
|
||||||
val idempotencyKey = getIdempotencyKey()
|
val idempotencyKey = getBlob(KEY_IDEMPOTENCY, null)?.let { IdempotencyKey.fromBytes(it) }
|
||||||
|
|
||||||
return if (level == null || idempotencyKey == null) {
|
return if (level == null || idempotencyKey == null) {
|
||||||
null
|
null
|
||||||
|
@ -130,19 +126,17 @@ internal class DonationsValues internal constructor(store: KeyValueStore) : Sign
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setLevelOperation(levelUpdateOperation: LevelUpdateOperation) {
|
fun setLevelOperation(levelUpdateOperation: LevelUpdateOperation) {
|
||||||
putString(KEY_LEVEL, levelUpdateOperation.level)
|
store.beginWrite()
|
||||||
setIdempotencyKey(levelUpdateOperation.idempotencyKey)
|
.putString(KEY_LEVEL, levelUpdateOperation.level)
|
||||||
dispatchLevelOperation()
|
.putBlob(KEY_IDEMPOTENCY, levelUpdateOperation.idempotencyKey.bytes)
|
||||||
|
.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearLevelOperation(levelUpdateOperation: LevelUpdateOperation): Boolean {
|
fun clearLevelOperation() {
|
||||||
val currentKey = getIdempotencyKey()
|
store.beginWrite()
|
||||||
return if (currentKey == levelUpdateOperation.idempotencyKey) {
|
.remove(KEY_LEVEL)
|
||||||
clearLevelOperation()
|
.remove(KEY_IDEMPOTENCY)
|
||||||
true
|
.apply()
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setExpiredBadge(badge: Badge?) {
|
fun setExpiredBadge(badge: Badge?) {
|
||||||
|
@ -159,20 +153,6 @@ internal class DonationsValues internal constructor(store: KeyValueStore) : Sign
|
||||||
return Badges.fromDatabaseBadge(BadgeList.Badge.parseFrom(badgeBytes))
|
return Badges.fromDatabaseBadge(BadgeList.Badge.parseFrom(badgeBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun clearLevelOperation() {
|
|
||||||
remove(KEY_IDEMPOTENCY)
|
|
||||||
remove(KEY_LEVEL)
|
|
||||||
dispatchLevelOperation()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getIdempotencyKey(): IdempotencyKey? {
|
|
||||||
return getBlob(KEY_IDEMPOTENCY, null)?.let { IdempotencyKey.fromBytes(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setIdempotencyKey(key: IdempotencyKey) {
|
|
||||||
putBlob(KEY_IDEMPOTENCY, key.bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getLastKeepAliveLaunchTime(): Long {
|
fun getLastKeepAliveLaunchTime(): Long {
|
||||||
return getLong(KEY_LAST_KEEP_ALIVE_LAUNCH, 0L)
|
return getLong(KEY_LAST_KEEP_ALIVE_LAUNCH, 0L)
|
||||||
}
|
}
|
||||||
|
@ -200,8 +180,4 @@ internal class DonationsValues internal constructor(store: KeyValueStore) : Sign
|
||||||
fun clearUserManuallyCancelled() {
|
fun clearUserManuallyCancelled() {
|
||||||
remove(USER_MANUALLY_CANCELLED)
|
remove(USER_MANUALLY_CANCELLED)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun dispatchLevelOperation() {
|
|
||||||
levelUpdateOperationPublisher.onNext(Optional.fromNullable(getLevelOperation()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
package org.thoughtcrime.securesms.subscription
|
||||||
|
|
||||||
|
import io.reactivex.rxjava3.core.Observable
|
||||||
|
import io.reactivex.rxjava3.subjects.BehaviorSubject
|
||||||
|
|
||||||
|
object LevelUpdate {
|
||||||
|
|
||||||
|
private var isProcessingSubject = BehaviorSubject.createDefault(false)
|
||||||
|
|
||||||
|
var isProcessing: Observable<Boolean> = isProcessingSubject
|
||||||
|
|
||||||
|
fun updateProcessingState(isProcessing: Boolean) {
|
||||||
|
isProcessingSubject.onNext(isProcessing)
|
||||||
|
}
|
||||||
|
}
|
Ładowanie…
Reference in New Issue