diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentsExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentsExtensions.kt index 6baa8a2efd..51032cd034 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentsExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentsExtensions.kt @@ -33,7 +33,6 @@ fun PaymentSource.toProto(): InAppPaymentSourceData { code = type.toInAppPaymentSourceDataCode(), idealData = if (this is IDEALPaymentSource) { InAppPaymentSourceData.IDEALData( - bank = idealData.bank, name = idealData.name, email = idealData.email ) @@ -77,8 +76,7 @@ fun InAppPaymentSourceData.toPaymentSource(): PaymentSource { InAppPaymentSourceData.Code.IDEAL -> { IDEALPaymentSource( StripeApi.IDEALData( - bank = idealData!!.bank, - name = idealData.name, + name = idealData!!.name, email = idealData.email ) ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/InAppPaymentError.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/InAppPaymentError.kt index 243f298e23..353c6bd1bd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/InAppPaymentError.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/InAppPaymentError.kt @@ -33,7 +33,7 @@ class InAppPaymentError( is DonationError.PaymentSetupError.StripeCodedError -> InAppPaymentData.Error(type = InAppPaymentData.Error.Type.STRIPE_CODED_ERROR, data_ = donationError.errorCode) is DonationError.PaymentSetupError.StripeDeclinedError -> InAppPaymentData.Error(type = InAppPaymentData.Error.Type.STRIPE_DECLINED_ERROR, data_ = donationError.declineCode.rawCode) is DonationError.PaymentSetupError.StripeFailureCodeError -> InAppPaymentData.Error(type = InAppPaymentData.Error.Type.STRIPE_FAILURE, data_ = donationError.failureCode.rawCode) - is DonationError.UserCancelledPaymentError -> null + is DonationError.UserCancelledPaymentError -> InAppPaymentData.Error(type = InAppPaymentData.Error.Type.SETUP_CANCELLED) is DonationError.UserLaunchedExternalApplication -> null } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/stripe/Stripe3DSDialogFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/stripe/Stripe3DSDialogFragment.kt index 89a3a194d6..12e111e9da 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/stripe/Stripe3DSDialogFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/stripe/Stripe3DSDialogFragment.kt @@ -33,6 +33,7 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.donate.Do import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.model.databaseprotos.InAppPaymentData import org.thoughtcrime.securesms.databinding.DonationWebviewFragmentBinding +import org.thoughtcrime.securesms.util.Environment import org.thoughtcrime.securesms.util.RemoteConfig import org.thoughtcrime.securesms.util.visible @@ -86,7 +87,7 @@ class Stripe3DSDialogFragment : DialogFragment(R.layout.donation_webview_fragmen ) ) - if (RemoteConfig.internalUser && args.waitingForAuthPayment.data.paymentMethodType == InAppPaymentData.PaymentMethodType.IDEAL) { + if (Environment.IS_STAGING && RemoteConfig.internalUser && args.waitingForAuthPayment.data.paymentMethodType == InAppPaymentData.PaymentMethodType.IDEAL) { val openApp = MaterialButton(requireContext()).apply { text = "Open App" layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/ideal/IdealBank.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/ideal/IdealBank.kt deleted file mode 100644 index 561c079266..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/ideal/IdealBank.kt +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2023 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.components.settings.app.subscription.donate.transfer.ideal - -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes -import org.thoughtcrime.securesms.R -import java.util.EnumMap - -/** - * Set of banks that are supported for iDEAL transfers, as listed here: - * https://stripe.com/docs/api/payment_methods/object#payment_method_object-ideal-bank - */ -enum class IdealBank( - val code: String -) { - ABN_AMRO("abn_amro"), - ASN_BANK("asn_bank"), - BUNQ("bunq"), - ING("ing"), - KNAB("knab"), - N26("n26"), - RABOBANK("rabobank"), - REGIOBANK("regiobank"), - REVOLUT("revolut"), - SNS_BANK("sns_bank"), - TRIODOS_BANK("triodos_bank"), - VAN_LANSCHOT("van_lanschot"), - YOURSAFE("yoursafe"); - - fun getUIValues(): UIValues = bankToUIValues[this]!! - - companion object { - - private val bankToUIValues: Map by lazy { - EnumMap(IdealBank::class.java).apply { - putAll( - arrayOf( - ABN_AMRO to UIValues( - name = R.string.IdealBank__abn_amro, - icon = R.drawable.ideal_abn_amro - ), - ASN_BANK to UIValues( - name = R.string.IdealBank__asn_bank, - icon = R.drawable.ideal_asn - ), - BUNQ to UIValues( - name = R.string.IdealBank__bunq, - icon = R.drawable.ideal_bunq - ), - ING to UIValues( - name = R.string.IdealBank__ing, - icon = R.drawable.ideal_ing - ), - KNAB to UIValues( - name = R.string.IdealBank__knab, - icon = R.drawable.ideal_knab - ), - N26 to UIValues( - name = R.string.IdealBank__n26, - icon = R.drawable.ideal_n26 - ), - RABOBANK to UIValues( - name = R.string.IdealBank__rabobank, - icon = R.drawable.ideal_rabobank - ), - REGIOBANK to UIValues( - name = R.string.IdealBank__regiobank, - icon = R.drawable.ideal_regiobank - ), - REVOLUT to UIValues( - name = R.string.IdealBank__revolut, - icon = R.drawable.ideal_revolut - ), - SNS_BANK to UIValues( - name = R.string.IdealBank__sns_bank, - icon = R.drawable.ideal_sns - ), - TRIODOS_BANK to UIValues( - name = R.string.IdealBank__triodos_bank, - icon = R.drawable.ideal_triodos_bank - ), - VAN_LANSCHOT to UIValues( - name = R.string.IdealBank__van_lanschot, - icon = R.drawable.ideal_van_lanschot - ), - YOURSAFE to UIValues( - name = R.string.IdealBank__yoursafe, - icon = R.drawable.ideal_yoursafe - ) - ) - ) - } - } - - fun fromCode(code: String): IdealBank { - return entries.first { it.code == code } - } - } - - data class UIValues( - @StringRes val name: Int, - @DrawableRes val icon: Int - ) -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/ideal/IdealTransferDetailsBankSelectionDialogFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/ideal/IdealTransferDetailsBankSelectionDialogFragment.kt deleted file mode 100644 index 5e1c124b0b..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/ideal/IdealTransferDetailsBankSelectionDialogFragment.kt +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2023 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.components.settings.app.subscription.donate.transfer.ideal - -import android.os.Bundle -import androidx.compose.foundation.Image -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.defaultMinSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment.Companion.CenterVertically -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.core.os.bundleOf -import androidx.fragment.app.setFragmentResult -import androidx.navigation.fragment.findNavController -import org.signal.core.ui.compose.Scaffolds -import org.thoughtcrime.securesms.R -import org.thoughtcrime.securesms.compose.ComposeDialogFragment -import org.signal.core.ui.R as CoreUiR - -/** - * Dialog fragment for selecting the bank for the iDEAL donation. - */ -class IdealTransferDetailsBankSelectionDialogFragment : ComposeDialogFragment() { - - companion object { - const val IDEAL_SELECTED_BANK = "ideal.selected.bank" - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - setStyle(STYLE_NO_FRAME, R.style.Signal_DayNight_Dialog_FullScreen) - } - - @Composable - override fun DialogContent() { - BankSelectionContent( - onNavigationClick = { findNavController().popBackStack() }, - onBankSelected = { - dismissAllowingStateLoss() - - setFragmentResult( - IDEAL_SELECTED_BANK, - bundleOf( - IDEAL_SELECTED_BANK to it.code - ) - ) - } - ) - } -} - -@Preview -@Composable -private fun BankSelectionContentPreview() { - BankSelectionContent( - onNavigationClick = {}, - onBankSelected = {} - ) -} - -@Composable -private fun BankSelectionContent( - onNavigationClick: () -> Unit, - onBankSelected: (IdealBank) -> Unit -) { - Scaffolds.Settings( - title = stringResource(R.string.IdealTransferDetailsBankSelectionDialogFragment__choose_your_bank), - onNavigationClick = onNavigationClick, - navigationIconPainter = painterResource(id = R.drawable.symbol_x_24) - ) { paddingValues -> - LazyColumn(modifier = Modifier.padding(paddingValues)) { - items(IdealBank.entries.toTypedArray()) { - val uiValues = it.getUIValues() - - Row( - verticalAlignment = CenterVertically, - modifier = Modifier - .clickable { onBankSelected(it) } - .fillMaxWidth() - .defaultMinSize(minHeight = 56.dp) - .padding(horizontal = dimensionResource(id = CoreUiR.dimen.gutter), vertical = 8.dp) - ) { - Image( - painter = painterResource(id = uiValues.icon), - contentDescription = null, - modifier = Modifier - .size(40.dp) - ) - - Text( - text = stringResource(uiValues.name), - modifier = Modifier.padding(start = 24.dp) - ) - } - } - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/ideal/IdealTransferDetailsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/ideal/IdealTransferDetailsFragment.kt index 86b01938e6..3ccb445f0d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/ideal/IdealTransferDetailsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/ideal/IdealTransferDetailsFragment.kt @@ -8,21 +8,16 @@ package org.thoughtcrime.securesms.components.settings.app.subscription.donate.t import android.os.Bundle import android.view.View import androidx.annotation.StringRes -import androidx.compose.foundation.Image -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextField -import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember @@ -30,16 +25,13 @@ import androidx.compose.ui.Alignment.Companion.CenterHorizontally import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusDirection import androidx.compose.ui.focus.onFocusChanged -import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.Role import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.core.os.bundleOf import androidx.fragment.app.setFragmentResult @@ -98,11 +90,6 @@ class IdealTransferDetailsFragment : ComposeFragment(), InAppPaymentCheckoutDele setFragmentResult(BankTransferRequestKeys.REQUEST_KEY, bundle) } } - - setFragmentResultListener(IdealTransferDetailsBankSelectionDialogFragment.IDEAL_SELECTED_BANK) { _, bundle -> - val bankCode = bundle.getString(IdealTransferDetailsBankSelectionDialogFragment.IDEAL_SELECTED_BANK)!! - viewModel.onBankSelected(IdealBank.fromCode(bankCode)) - } } @Composable @@ -166,7 +153,7 @@ class IdealTransferDetailsFragment : ComposeFragment(), InAppPaymentCheckoutDele if (state.inAppPayment!!.type.recurring) { // TODO [message-requests] -- handle backup val formattedMoney = FiatMoneyUtil.format(requireContext().resources, state.inAppPayment.data.amount!!.toFiatMoney(), FiatMoneyUtil.formatOptions().trimZerosAfterDecimal()) MaterialAlertDialogBuilder(requireContext()) - .setTitle(getString(R.string.IdealTransferDetailsFragment__confirm_your_donation_with_s, getString(state.idealBank!!.getUIValues().name))) + .setTitle(getString(R.string.IdealTransferDetailsFragment__confirm_your_donation_with_ideal)) .setMessage(getString(R.string.IdealTransferDetailsFragment__to_setup_your_recurring_donation, formattedMoney)) .setPositiveButton(R.string.IdealTransferDetailsFragment__continue) { _, _ -> continueTransfer() @@ -262,15 +249,6 @@ private fun IdealTransferDetailsContent( ) } - item { - IdealBankSelector( - idealBank = state.idealBank, - onSelectBankClick = onSelectBankClick, - modifier = Modifier - .fillMaxWidth() - ) - } - item { TextField( value = state.name, @@ -346,60 +324,3 @@ private fun IdealTransferDetailsContent( } } } - -@Preview -@Composable -private fun IdealBankSelectorPreview() { - IdealBankSelector( - idealBank = null, - onSelectBankClick = {} - ) -} - -@Composable -private fun IdealBankSelector( - idealBank: IdealBank?, - onSelectBankClick: () -> Unit, - modifier: Modifier = Modifier -) { - val uiValues: IdealBank.UIValues? = remember(idealBank) { idealBank?.getUIValues() } - val imagePadding: Dp = if (idealBank == null) 4.dp else 0.dp - - TextField( - value = stringResource(id = uiValues?.name ?: R.string.IdealTransferDetailsFragment__choose_your_bank), - textStyle = MaterialTheme.typography.bodyLarge, - onValueChange = {}, - enabled = false, - readOnly = true, - leadingIcon = { - Image( - painter = painterResource(id = uiValues?.icon ?: R.drawable.bank_transfer), - contentDescription = null, - colorFilter = if (uiValues?.icon == null) ColorFilter.tint(MaterialTheme.colorScheme.onSurface) else null, - modifier = Modifier - .padding(start = 16.dp, end = 12.dp) - .size(32.dp) - .padding(imagePadding) - ) - }, - trailingIcon = { - Icon( - painter = painterResource(id = R.drawable.symbol_dropdown_triangle_compat_bold_16), - contentDescription = null - ) - }, - colors = TextFieldDefaults.colors( - disabledTextColor = MaterialTheme.colorScheme.onSurface, - disabledTrailingIconColor = MaterialTheme.colorScheme.onSurface, - disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant, - disabledIndicatorColor = MaterialTheme.colorScheme.onSurface - ), - supportingText = {}, - modifier = modifier - .defaultMinSize(minHeight = 78.dp) - .clickable( - onClick = onSelectBankClick, - role = Role.Button - ) - ) -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/ideal/IdealTransferDetailsState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/ideal/IdealTransferDetailsState.kt index 4df72aca1e..f6f068cfc6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/ideal/IdealTransferDetailsState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/ideal/IdealTransferDetailsState.kt @@ -11,7 +11,6 @@ import org.thoughtcrime.securesms.database.InAppPaymentTable data class IdealTransferDetailsState( val inAppPayment: InAppPaymentTable.InAppPayment? = null, - val idealBank: IdealBank? = null, val name: String = "", val nameFocusState: FocusState = FocusState.NOT_FOCUSED, val email: String = "", @@ -28,14 +27,13 @@ data class IdealTransferDetailsState( fun asIDEALData(): StripeApi.IDEALData { return StripeApi.IDEALData( - bank = idealBank!!.code, name = name.trim(), email = email.trim() ) } fun canProceed(): Boolean { - return idealBank != null && BankDetailsValidator.validName(name) && (inAppPayment?.type?.recurring != true || BankDetailsValidator.validEmail(email)) + return BankDetailsValidator.validName(name) && (inAppPayment?.type?.recurring != true || BankDetailsValidator.validEmail(email)) } enum class FocusState { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/ideal/IdealTransferDetailsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/ideal/IdealTransferDetailsViewModel.kt index c13be6354c..88e165fdcf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/ideal/IdealTransferDetailsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/ideal/IdealTransferDetailsViewModel.kt @@ -71,12 +71,6 @@ class IdealTransferDetailsViewModel(inAppPaymentId: InAppPaymentTable.InAppPayme } } - fun onBankSelected(idealBank: IdealBank) { - internalState.value = internalState.value.copy( - idealBank = idealBank - ) - } - enum class Field { NAME, EMAIL diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/mandate/BankTransferMandateRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/mandate/BankTransferMandateRepository.kt index c3b2d2cf32..100ae4fcc4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/mandate/BankTransferMandateRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/transfer/mandate/BankTransferMandateRepository.kt @@ -14,8 +14,14 @@ import java.util.Locale object BankTransferMandateRepository { fun getMandate(paymentSourceType: PaymentSourceType.Stripe): Single { + val sourceString = if (paymentSourceType == PaymentSourceType.Stripe.IDEAL) { + PaymentSourceType.Stripe.SEPADebit.paymentMethod + } else { + paymentSourceType.paymentMethod + } + return Single - .fromCallable { AppDependencies.donationsService.getBankMandate(Locale.getDefault(), paymentSourceType.paymentMethod) } + .fromCallable { AppDependencies.donationsService.getBankMandate(Locale.getDefault(), sourceString) } .flatMap { it.flattenResult() } .map { it.mandate } .subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/errors/DonationErrorParams.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/errors/DonationErrorParams.kt index 4208ac7d30..5763f32f24 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/errors/DonationErrorParams.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/errors/DonationErrorParams.kt @@ -85,6 +85,7 @@ class DonationErrorParams private constructor( InAppPaymentData.Error.Type.PAYMENT_PROCESSING -> getGenericRedemptionError(context, inAppPayment.type, callback) InAppPaymentData.Error.Type.CREDENTIAL_VALIDATION -> getBadgeCredentialValidationErrorParams(context, callback) InAppPaymentData.Error.Type.REDEMPTION -> getGenericRedemptionError(context, inAppPayment.type, callback) + InAppPaymentData.Error.Type.SETUP_CANCELLED -> error("Shouldn't show a dialog.") null -> error("No error in data!") } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentRecurringContextJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentRecurringContextJob.kt index a575d1bed2..a7190d88b9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentRecurringContextJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentRecurringContextJob.kt @@ -102,7 +102,12 @@ class InAppPaymentRecurringContextJob private constructor( warning("A permanent failure occurred.") val inAppPayment = SignalDatabase.inAppPayments.getById(inAppPaymentId) - if (inAppPayment != null && inAppPayment.data.error == null) { + val isRedeemed = inAppPayment?.state == InAppPaymentTable.State.END && inAppPayment.data.redemption?.stage != InAppPaymentData.RedemptionState.Stage.REDEEMED + if (isRedeemed) { + info("Already redeemed. Exiting quietly.") + return + } else if (inAppPayment != null && inAppPayment.data.error == null) { + warning("Unredeemed payment failed.") SignalDatabase.inAppPayments.update( inAppPayment.copy( notified = false, diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentSetupJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentSetupJob.kt index bbe77a1ea2..a74b6a9d4d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentSetupJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentSetupJob.kt @@ -204,7 +204,7 @@ abstract class InAppPaymentSetupJob( } } - private fun handleFailure(inAppPaymentId: InAppPaymentTable.InAppPaymentId, exception: Exception) { + protected fun handleFailure(inAppPaymentId: InAppPaymentTable.InAppPaymentId, exception: Exception) { warning("Failed to process transaction.", exception) val freshPayment = SignalDatabase.inAppPayments.getById(inAppPaymentId)!! diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentStripeOneTimeSetupJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentStripeOneTimeSetupJob.kt index ab7b8939e8..8eb0101aff 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentStripeOneTimeSetupJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentStripeOneTimeSetupJob.kt @@ -12,6 +12,8 @@ import org.signal.donations.StripeIntentAccessor import org.thoughtcrime.securesms.components.settings.app.subscription.DonationSerializationHelper.toFiatMoney import org.thoughtcrime.securesms.components.settings.app.subscription.OneTimeInAppPaymentRepository import org.thoughtcrime.securesms.components.settings.app.subscription.StripeRepository +import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationError +import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationErrorSource import org.thoughtcrime.securesms.components.settings.app.subscription.toPaymentSource import org.thoughtcrime.securesms.database.InAppPaymentTable import org.thoughtcrime.securesms.jobmanager.Job @@ -74,7 +76,13 @@ class InAppPaymentStripeOneTimeSetupJob private constructor( ) info("Getting status and payment method id from stripe.") - StripeRepository.getStatusAndPaymentMethodId(intentAccessor, paymentMethodId) + val data = StripeRepository.getStatusAndPaymentMethodId(intentAccessor, paymentMethodId) + + if (!data.status.canProceed()) { + warning("Cannot proceed with status ${data.status}.") + handleFailure(inAppPayment.id, DonationError.UserCancelledPaymentError(DonationErrorSource.ONE_TIME)) + return Result.failure() + } info("Received status and payment method id. Submitting redemption job chain.") OneTimeInAppPaymentRepository.submitRedemptionJobChain(inAppPayment, intentAccessor.intentId) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentStripeRecurringSetupJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentStripeRecurringSetupJob.kt index bb978460dc..d15c6c487e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentStripeRecurringSetupJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentStripeRecurringSetupJob.kt @@ -16,6 +16,8 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaym import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentsRepository.toPaymentSourceType import org.thoughtcrime.securesms.components.settings.app.subscription.RecurringInAppPaymentRepository import org.thoughtcrime.securesms.components.settings.app.subscription.StripeRepository +import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationError +import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationErrorSource import org.thoughtcrime.securesms.components.settings.app.subscription.toPaymentSource import org.thoughtcrime.securesms.database.InAppPaymentTable import org.thoughtcrime.securesms.jobmanager.Job @@ -79,6 +81,12 @@ class InAppPaymentStripeRecurringSetupJob private constructor( info("Requesting status and payment method id from stripe service.") val statusAndPaymentMethodId = StripeRepository.getStatusAndPaymentMethodId(intentAccessor, paymentMethodId) + if (!statusAndPaymentMethodId.status.canProceed()) { + warning("Cannot proceed with status ${statusAndPaymentMethodId.status}.") + handleFailure(inAppPayment.id, DonationError.UserCancelledPaymentError(DonationErrorSource.ONE_TIME)) + return Result.failure() + } + info("Setting default payment method.") StripeRepository.setDefaultPaymentMethod( paymentMethodId = statusAndPaymentMethodId.paymentMethod!!, diff --git a/app/src/main/protowire/Database.proto b/app/src/main/protowire/Database.proto index e57edc911e..811ea8f77d 100644 --- a/app/src/main/protowire/Database.proto +++ b/app/src/main/protowire/Database.proto @@ -414,13 +414,14 @@ message InAppPaymentData { INVALID_CURRENCY = 5; // One-time payment currency is invalid PAYMENT_SETUP = 6; // A generic payment setup error (prior to charging the user) STRIPE_CODED_ERROR = 7; // Stripe error containing a stripe error code in data. - STRIPE_DECLINED_ERROR = 8; // Stripe error containing a decline error code in data. - STRIPE_FAILURE = 9; // Stripe error containing a failure error code in data. + STRIPE_DECLINED_ERROR = 8; // Stripe error containing a decline error code in data. + STRIPE_FAILURE = 9; // Stripe error containing a failure error code in data. PAYPAL_CODED_ERROR = 10; // PayPal error containing a paypal error code in data. PAYPAL_DECLINED_ERROR = 11; // PayPal error containing a paypal decline code in data. PAYMENT_PROCESSING = 12; // Generic payment error containing an HTTP status code in data. CREDENTIAL_VALIDATION = 13; // Failed to validate credential returned from service. REDEMPTION = 14; // Failed during badge redemption containing an HTTP status code in data if available. + SETUP_CANCELLED = 15; // After an authorization attempt, the intent is still not set up correctly. } Type type = 1; diff --git a/app/src/main/protowire/JobData.proto b/app/src/main/protowire/JobData.proto index 7102045dea..3ee608c997 100644 --- a/app/src/main/protowire/JobData.proto +++ b/app/src/main/protowire/JobData.proto @@ -190,7 +190,7 @@ message InAppPaymentSourceData { } message IDEALData { - string bank = 1; + reserved 1; // bank, no longer utilized. string name = 2; string email = 3; } diff --git a/app/src/main/res/drawable/ideal_abn_amro.xml b/app/src/main/res/drawable/ideal_abn_amro.xml deleted file mode 100644 index e0c3ba03a9..0000000000 --- a/app/src/main/res/drawable/ideal_abn_amro.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/drawable/ideal_asn.xml b/app/src/main/res/drawable/ideal_asn.xml deleted file mode 100644 index 13952f7bc1..0000000000 --- a/app/src/main/res/drawable/ideal_asn.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/drawable/ideal_bunq.xml b/app/src/main/res/drawable/ideal_bunq.xml deleted file mode 100644 index 8698f20664..0000000000 --- a/app/src/main/res/drawable/ideal_bunq.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - diff --git a/app/src/main/res/drawable/ideal_ing.xml b/app/src/main/res/drawable/ideal_ing.xml deleted file mode 100644 index 9c07e39765..0000000000 --- a/app/src/main/res/drawable/ideal_ing.xml +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ideal_knab.xml b/app/src/main/res/drawable/ideal_knab.xml deleted file mode 100644 index 3c4c0d953d..0000000000 --- a/app/src/main/res/drawable/ideal_knab.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/drawable/ideal_n26.xml b/app/src/main/res/drawable/ideal_n26.xml deleted file mode 100644 index f952abf410..0000000000 --- a/app/src/main/res/drawable/ideal_n26.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/ideal_rabobank.xml b/app/src/main/res/drawable/ideal_rabobank.xml deleted file mode 100644 index 4a9c71ced9..0000000000 --- a/app/src/main/res/drawable/ideal_rabobank.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ideal_regiobank.xml b/app/src/main/res/drawable/ideal_regiobank.xml deleted file mode 100644 index 0c820802d1..0000000000 --- a/app/src/main/res/drawable/ideal_regiobank.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/drawable/ideal_revolut.xml b/app/src/main/res/drawable/ideal_revolut.xml deleted file mode 100644 index c30e83242f..0000000000 --- a/app/src/main/res/drawable/ideal_revolut.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/drawable/ideal_sns.xml b/app/src/main/res/drawable/ideal_sns.xml deleted file mode 100644 index fe60e96e2c..0000000000 --- a/app/src/main/res/drawable/ideal_sns.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/ideal_triodos_bank.xml b/app/src/main/res/drawable/ideal_triodos_bank.xml deleted file mode 100644 index c819722017..0000000000 --- a/app/src/main/res/drawable/ideal_triodos_bank.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/drawable/ideal_van_lanschot.xml b/app/src/main/res/drawable/ideal_van_lanschot.xml deleted file mode 100644 index 7b3cf39200..0000000000 --- a/app/src/main/res/drawable/ideal_van_lanschot.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/ideal_yoursafe.xml b/app/src/main/res/drawable/ideal_yoursafe.xml deleted file mode 100644 index b3febead3d..0000000000 --- a/app/src/main/res/drawable/ideal_yoursafe.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3643d4c613..fdf16ab037 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7165,34 +7165,6 @@ Leave Signal to confirm donation? When confirmed, return to Signal to finish processing your donation. - - - ABN AMRO - - ASN Bank - - bunq - - ING - - Knab - - N26 - - Rabobank - - RegioBank - - Revolut - - SNS Bank - - Triodos Bank - - Van Lanschot Kempen - - Yoursafe - Bank Transfer @@ -7253,7 +7225,7 @@ Choose your bank - Confirm your donation with %1$s + Confirm your donation with iDEAL To setup your recurring donation tap continue to confirm a €0,01 charge with your bank. This will be automatically refunded and allows your %1$s/month donation to debited from your account. diff --git a/donations/lib/src/main/java/org/signal/donations/StripeApi.kt b/donations/lib/src/main/java/org/signal/donations/StripeApi.kt index 9a672ba69c..be55ebc0cd 100644 --- a/donations/lib/src/main/java/org/signal/donations/StripeApi.kt +++ b/donations/lib/src/main/java/org/signal/donations/StripeApi.kt @@ -265,7 +265,6 @@ class StripeApi( private fun createPaymentMethodForIDEAL(paymentSource: IDEALPaymentSource): Response { val parameters = mutableMapOf( "type" to "ideal", - "ideal[bank]" to paymentSource.idealData.bank, "billing_details[email]" to paymentSource.idealData.email, "billing_details[name]" to paymentSource.idealData.name ) @@ -606,7 +605,6 @@ class StripeApi( @Parcelize data class IDEALData( - val bank: String, val name: String, val email: String ) : Parcelable diff --git a/donations/lib/src/main/java/org/signal/donations/json/StripeIntentStatus.kt b/donations/lib/src/main/java/org/signal/donations/json/StripeIntentStatus.kt index 5976162559..a2d8bc23f4 100644 --- a/donations/lib/src/main/java/org/signal/donations/json/StripeIntentStatus.kt +++ b/donations/lib/src/main/java/org/signal/donations/json/StripeIntentStatus.kt @@ -26,6 +26,10 @@ enum class StripeIntentStatus(private val code: String) { fun fromCode(code: String): StripeIntentStatus = entries.first { it.code == code } } + fun canProceed(): Boolean { + return this == PROCESSING || this == SUCCEEDED + } + @JsonValue fun toValue(): String { return code