From 84d028371993673055ed971502d00a7e65f8a206 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Tue, 10 Jan 2023 12:57:03 -0400 Subject: [PATCH] Ensure capability check occurs for gift recipients. --- .../badges/gifts/flow/GiftFlowRepository.kt | 43 ------------------- .../subscription/OneTimeDonationRepository.kt | 34 +++++++++++++++ .../PayPalPaymentInProgressViewModel.kt | 18 +++++--- .../StripePaymentInProgressViewModel.kt | 7 ++- 4 files changed, 53 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/flow/GiftFlowRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/flow/GiftFlowRepository.kt index 9958083b8..d066cd5ad 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/flow/GiftFlowRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/flow/GiftFlowRepository.kt @@ -1,22 +1,14 @@ package org.thoughtcrime.securesms.badges.gifts.flow -import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.schedulers.Schedulers import org.signal.core.util.logging.Log import org.signal.core.util.money.FiatMoney import org.thoughtcrime.securesms.badges.models.Badge -import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationError import org.thoughtcrime.securesms.components.settings.app.subscription.getGiftBadgeAmounts import org.thoughtcrime.securesms.components.settings.app.subscription.getGiftBadges -import org.thoughtcrime.securesms.database.RecipientTable import org.thoughtcrime.securesms.dependencies.ApplicationDependencies -import org.thoughtcrime.securesms.recipients.Recipient -import org.thoughtcrime.securesms.recipients.RecipientId -import org.thoughtcrime.securesms.util.ProfileUtil -import org.whispersystems.signalservice.api.profiles.SignalServiceProfile import org.whispersystems.signalservice.internal.push.DonationsConfiguration -import java.io.IOException import java.util.Currency import java.util.Locale @@ -50,39 +42,4 @@ class GiftFlowRepository { .flatMap { it.flattenResult() } .map { it.getGiftBadgeAmounts() } } - - /** - * Verifies that the given recipient is a supported target for a gift. - * - * TODO[alex] - this needs to be incorporated into the correct flows. - */ - fun verifyRecipientIsAllowedToReceiveAGift(badgeRecipient: RecipientId): Completable { - return Completable.fromAction { - Log.d(TAG, "Verifying badge recipient $badgeRecipient", true) - val recipient = Recipient.resolved(badgeRecipient) - - if (recipient.isSelf) { - Log.d(TAG, "Cannot send a gift to self.", true) - throw DonationError.GiftRecipientVerificationError.SelectedRecipientDoesNotSupportGifts - } - - if (recipient.isGroup || recipient.isDistributionList || recipient.registered != RecipientTable.RegisteredState.REGISTERED) { - Log.w(TAG, "Invalid badge recipient $badgeRecipient. Verification failed.", true) - throw DonationError.GiftRecipientVerificationError.SelectedRecipientIsInvalid - } - - try { - val profile = ProfileUtil.retrieveProfileSync(ApplicationDependencies.getApplication(), recipient, SignalServiceProfile.RequestType.PROFILE_AND_CREDENTIAL) - if (!profile.profile.capabilities.isGiftBadges) { - Log.w(TAG, "Badge recipient does not support gifting. Verification failed.", true) - throw DonationError.GiftRecipientVerificationError.SelectedRecipientDoesNotSupportGifts - } else { - Log.d(TAG, "Badge recipient supports gifting. Verification successful.", true) - } - } catch (e: IOException) { - Log.w(TAG, "Failed to retrieve profile for recipient.", e, true) - throw DonationError.GiftRecipientVerificationError.FailedToFetchProfile(e) - } - }.subscribeOn(Schedulers.io()) - } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/OneTimeDonationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/OneTimeDonationRepository.kt index ff361ed67..65450e0e0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/OneTimeDonationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/OneTimeDonationRepository.kt @@ -10,6 +10,7 @@ import org.thoughtcrime.securesms.badges.models.Badge import org.thoughtcrime.securesms.components.settings.app.subscription.boost.Boost import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationError import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationErrorSource +import org.thoughtcrime.securesms.database.RecipientTable import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.model.DonationReceiptRecord import org.thoughtcrime.securesms.dependencies.ApplicationDependencies @@ -17,8 +18,11 @@ import org.thoughtcrime.securesms.jobmanager.JobTracker import org.thoughtcrime.securesms.jobs.BoostReceiptRequestResponseJob import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.util.ProfileUtil +import org.whispersystems.signalservice.api.profiles.SignalServiceProfile import org.whispersystems.signalservice.api.services.DonationsService import org.whispersystems.signalservice.internal.push.DonationProcessor +import java.io.IOException import java.util.Currency import java.util.Locale import java.util.concurrent.CountDownLatch @@ -38,6 +42,36 @@ class OneTimeDonationRepository(private val donationsService: DonationsService) Single.error(DonationError.getPaymentSetupError(errorSource, throwable, paymentSourceType)) } } + + fun verifyRecipientIsAllowedToReceiveAGift(badgeRecipient: RecipientId): Completable { + return Completable.fromAction { + Log.d(TAG, "Verifying badge recipient $badgeRecipient", true) + val recipient = Recipient.resolved(badgeRecipient) + + if (recipient.isSelf) { + Log.d(TAG, "Cannot send a gift to self.", true) + throw DonationError.GiftRecipientVerificationError.SelectedRecipientDoesNotSupportGifts + } + + if (recipient.isGroup || recipient.isDistributionList || recipient.registered != RecipientTable.RegisteredState.REGISTERED) { + Log.w(TAG, "Invalid badge recipient $badgeRecipient. Verification failed.", true) + throw DonationError.GiftRecipientVerificationError.SelectedRecipientIsInvalid + } + + try { + val profile = ProfileUtil.retrieveProfileSync(ApplicationDependencies.getApplication(), recipient, SignalServiceProfile.RequestType.PROFILE_AND_CREDENTIAL) + if (!profile.profile.capabilities.isGiftBadges) { + Log.w(TAG, "Badge recipient does not support gifting. Verification failed.", true) + throw DonationError.GiftRecipientVerificationError.SelectedRecipientDoesNotSupportGifts + } else { + Log.d(TAG, "Badge recipient supports gifting. Verification successful.", true) + } + } catch (e: IOException) { + Log.w(TAG, "Failed to retrieve profile for recipient.", e, true) + throw DonationError.GiftRecipientVerificationError.FailedToFetchProfile(e) + } + }.subscribeOn(Schedulers.io()) + } } fun getBoosts(): Single>> { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/paypal/PayPalPaymentInProgressViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/paypal/PayPalPaymentInProgressViewModel.kt index f35fdd9da..d8b498e9f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/paypal/PayPalPaymentInProgressViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/paypal/PayPalPaymentInProgressViewModel.kt @@ -127,12 +127,20 @@ class PayPalPaymentInProgressViewModel( ) { Log.d(TAG, "Proceeding with one-time payment pipeline...", true) store.update { DonationProcessorStage.PAYMENT_PIPELINE } + val verifyUser = if (request.donateToSignalType == DonateToSignalType.GIFT) { + OneTimeDonationRepository.verifyRecipientIsAllowedToReceiveAGift(request.recipientId) + } else { + Completable.complete() + } - disposables += payPalRepository - .createOneTimePaymentIntent( - amount = request.fiat, - badgeRecipient = request.recipientId, - badgeLevel = request.level + disposables += verifyUser + .andThen( + payPalRepository + .createOneTimePaymentIntent( + amount = request.fiat, + badgeRecipient = request.recipientId, + badgeLevel = request.level + ) ) .flatMap(routeToPaypalConfirmation) .flatMap { result -> diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/stripe/StripePaymentInProgressViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/stripe/StripePaymentInProgressViewModel.kt index 9541b0b34..c4a0df24f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/stripe/StripePaymentInProgressViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/stripe/StripePaymentInProgressViewModel.kt @@ -178,8 +178,13 @@ class StripePaymentInProgressViewModel( Log.w(TAG, "Beginning one-time payment pipeline...", true) val amount = request.fiat + val verifyUser = if (request.donateToSignalType == DonateToSignalType.GIFT) { + OneTimeDonationRepository.verifyRecipientIsAllowedToReceiveAGift(request.recipientId) + } else { + Completable.complete() + } - val continuePayment: Single = stripeRepository.continuePayment(amount, request.recipientId, request.level, paymentSourceProvider.paymentSourceType) + val continuePayment: Single = verifyUser.andThen(stripeRepository.continuePayment(amount, request.recipientId, request.level, paymentSourceProvider.paymentSourceType)) val intentAndSource: Single> = Single.zip(continuePayment, paymentSourceProvider.paymentSource, ::Pair) disposables += intentAndSource.flatMapCompletable { (paymentIntent, paymentSource) ->