Add new error strings for credit cards.

main
Alex Hart 2022-11-29 11:01:07 -04:00 zatwierdzone przez GitHub
rodzic f6356c9720
commit eee4ff3f87
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
14 zmienionych plików z 200 dodań i 34 usunięć

Wyświetl plik

@ -11,6 +11,7 @@ import org.signal.core.util.money.FiatMoney
import org.signal.donations.GooglePayApi
import org.signal.donations.StripeApi
import org.signal.donations.StripeIntentAccessor
import org.signal.donations.StripePaymentSourceType
import org.signal.donations.json.StripeIntentStatus
import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationError
import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationErrorSource
@ -86,12 +87,13 @@ class StripeRepository(activity: Activity) : StripeApi.PaymentIntentFetcher, Str
price: FiatMoney,
badgeRecipient: RecipientId,
badgeLevel: Long,
paymentSourceType: StripePaymentSourceType
): Single<StripeIntentAccessor> {
Log.d(TAG, "Creating payment intent for $price...", true)
return stripeApi.createPaymentIntent(price, badgeLevel)
.onErrorResumeNext {
handleCreatePaymentIntentError(it, badgeRecipient)
handleCreatePaymentIntentError(it, badgeRecipient, paymentSourceType)
}
.flatMap { result ->
val recipient = Recipient.resolved(badgeRecipient)
@ -127,7 +129,7 @@ class StripeRepository(activity: Activity) : StripeApi.PaymentIntentFetcher, Str
Log.d(TAG, "Confirming payment intent...", true)
return stripeApi.confirmPaymentIntent(paymentSource, paymentIntent)
.onErrorResumeNext {
Single.error(DonationError.getPaymentSetupError(donationErrorSource, it))
Single.error(DonationError.getPaymentSetupError(donationErrorSource, it, paymentSource.type))
}
}
@ -196,7 +198,10 @@ class StripeRepository(activity: Activity) : StripeApi.PaymentIntentFetcher, Str
}
}
fun setDefaultPaymentMethod(paymentMethodId: String): Completable {
fun setDefaultPaymentMethod(
paymentMethodId: String,
paymentSourceType: StripePaymentSourceType
): Completable {
return Single.fromCallable {
Log.d(TAG, "Getting the subscriber...")
SignalStore.donationsValues().requireSubscriber()
@ -209,6 +214,9 @@ class StripeRepository(activity: Activity) : StripeApi.PaymentIntentFetcher, Str
}
}.flatMap(ServiceResponse<EmptyResponse>::flattenResult).ignoreElement().doOnComplete {
Log.d(TAG, "Set default payment method via Signal service!")
}.andThen {
Log.d(TAG, "Storing the subscription payment source type locally.")
SignalStore.donationsValues().setSubscriptionPaymentSourceType(paymentSourceType)
}
}
@ -216,7 +224,7 @@ class StripeRepository(activity: Activity) : StripeApi.PaymentIntentFetcher, Str
Log.d(TAG, "Creating credit card payment source via Stripe api...")
return stripeApi.createPaymentSourceFromCardData(cardData).map {
when (it) {
is StripeApi.CreatePaymentSourceFromCardDataResult.Failure -> throw DonationError.getPaymentSetupError(donationErrorSource, it.reason)
is StripeApi.CreatePaymentSourceFromCardDataResult.Failure -> throw DonationError.getPaymentSetupError(donationErrorSource, it.reason, StripePaymentSourceType.CREDIT_CARD)
is StripeApi.CreatePaymentSourceFromCardDataResult.Success -> it.paymentSource
}
}
@ -230,13 +238,13 @@ class StripeRepository(activity: Activity) : StripeApi.PaymentIntentFetcher, Str
companion object {
private val TAG = Log.tag(StripeRepository::class.java)
fun <T> handleCreatePaymentIntentError(throwable: Throwable, badgeRecipient: RecipientId): Single<T> {
private fun <T> handleCreatePaymentIntentError(throwable: Throwable, badgeRecipient: RecipientId, paymentSourceType: StripePaymentSourceType): Single<T> {
return if (throwable is DonationError) {
Single.error(throwable)
} else {
val recipient = Recipient.resolved(badgeRecipient)
val errorSource = if (recipient.isSelf) DonationErrorSource.BOOST else DonationErrorSource.GIFT
Single.error(DonationError.getPaymentSetupError(errorSource, throwable))
Single.error(DonationError.getPaymentSetupError(errorSource, throwable, paymentSourceType))
}
}
}

Wyświetl plik

@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.components.settings.app.subscription.donate
import android.content.Context
import android.content.DialogInterface
import android.text.SpannableStringBuilder
import android.view.View
@ -31,6 +32,7 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.boost.Boo
import org.thoughtcrime.securesms.components.settings.app.subscription.donate.gateway.GatewayRequest
import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationError
import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationErrorDialogs
import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationErrorParams
import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationErrorSource
import org.thoughtcrime.securesms.components.settings.app.subscription.models.CurrencySelection
import org.thoughtcrime.securesms.components.settings.app.subscription.models.NetworkFailure
@ -395,9 +397,22 @@ class DonateToSignalFragment :
errorDialog = DonationErrorDialogs.show(
requireContext(), throwable,
object : DonationErrorDialogs.DialogCallback() {
var tryCCAgain = false
override fun onTryCreditCardAgain(context: Context): DonationErrorParams.ErrorAction<Unit>? {
return DonationErrorParams.ErrorAction(
label = R.string.DeclineCode__try,
action = {
tryCCAgain = true
}
)
}
override fun onDialogDismissed() {
errorDialog = null
findNavController().popBackStack()
if (!tryCCAgain) {
findNavController().popBackStack()
}
}
}
)

Wyświetl plik

@ -14,6 +14,7 @@ import org.signal.core.util.logging.Log
import org.signal.donations.GooglePayPaymentSource
import org.signal.donations.StripeApi
import org.signal.donations.StripeIntentAccessor
import org.signal.donations.StripePaymentSourceType
import org.thoughtcrime.securesms.components.settings.app.subscription.MonthlyDonationRepository
import org.thoughtcrime.securesms.components.settings.app.subscription.OneTimeDonationRepository
import org.thoughtcrime.securesms.components.settings.app.subscription.StripeRepository
@ -75,7 +76,7 @@ class StripePaymentInProgressViewModel(
DonateToSignalType.GIFT -> DonationErrorSource.GIFT
}
val paymentSourceProvider: Single<StripeApi.PaymentSource> = resolvePaymentSourceProvider(errorSource)
val paymentSourceProvider: PaymentSourceProvider = resolvePaymentSourceProvider(errorSource)
return when (request.donateToSignalType) {
DonateToSignalType.MONTHLY -> proceedMonthly(request, paymentSourceProvider, nextActionHandler)
@ -84,17 +85,23 @@ class StripePaymentInProgressViewModel(
}
}
private fun resolvePaymentSourceProvider(errorSource: DonationErrorSource): Single<StripeApi.PaymentSource> {
private fun resolvePaymentSourceProvider(errorSource: DonationErrorSource): PaymentSourceProvider {
val paymentData = this.paymentData
val cardData = this.cardData
return when {
paymentData == null && cardData == null -> error("No payment provider available.")
paymentData != null && cardData != null -> error("Too many providers available")
paymentData != null -> Single.just<StripeApi.PaymentSource>(GooglePayPaymentSource(paymentData))
cardData != null -> stripeRepository.createCreditCardPaymentSource(errorSource, cardData)
paymentData != null -> PaymentSourceProvider(
StripePaymentSourceType.GOOGLE_PAY,
Single.just<StripeApi.PaymentSource>(GooglePayPaymentSource(paymentData)).doAfterTerminate { clearPaymentInformation() }
)
cardData != null -> PaymentSourceProvider(
StripePaymentSourceType.CREDIT_CARD,
stripeRepository.createCreditCardPaymentSource(errorSource, cardData).doAfterTerminate { clearPaymentInformation() }
)
else -> error("This should never happen.")
}.doAfterTerminate { clearPaymentInformation() }
}
}
fun providePaymentData(paymentData: PaymentData) {
@ -118,9 +125,9 @@ class StripePaymentInProgressViewModel(
cardData = null
}
private fun proceedMonthly(request: GatewayRequest, paymentSourceProvider: Single<StripeApi.PaymentSource>, nextActionHandler: (StripeApi.Secure3DSAction) -> Single<StripeIntentAccessor>) {
private fun proceedMonthly(request: GatewayRequest, paymentSourceProvider: PaymentSourceProvider, nextActionHandler: (StripeApi.Secure3DSAction) -> Single<StripeIntentAccessor>) {
val ensureSubscriberId: Completable = monthlyDonationRepository.ensureSubscriberId()
val createAndConfirmSetupIntent: Single<StripeApi.Secure3DSAction> = paymentSourceProvider.flatMap { stripeRepository.createAndConfirmSetupIntent(it) }
val createAndConfirmSetupIntent: Single<StripeApi.Secure3DSAction> = paymentSourceProvider.paymentSource.flatMap { stripeRepository.createAndConfirmSetupIntent(it) }
val setLevel: Completable = monthlyDonationRepository.setSubscriptionLevel(request.level.toString())
Log.d(TAG, "Starting subscription payment pipeline...", true)
@ -134,12 +141,12 @@ class StripePaymentInProgressViewModel(
.flatMap { secure3DSResult -> stripeRepository.getStatusAndPaymentMethodId(secure3DSResult) }
.map { (_, paymentMethod) -> paymentMethod ?: secure3DSAction.paymentMethodId!! }
}
.flatMapCompletable { stripeRepository.setDefaultPaymentMethod(it) }
.flatMapCompletable { stripeRepository.setDefaultPaymentMethod(it, paymentSourceProvider.paymentSourceType) }
.onErrorResumeNext {
if (it is DonationError) {
Completable.error(it)
} else {
Completable.error(DonationError.getPaymentSetupError(DonationErrorSource.SUBSCRIPTION, it))
Completable.error(DonationError.getPaymentSetupError(DonationErrorSource.SUBSCRIPTION, it, paymentSourceProvider.paymentSourceType))
}
}
@ -164,15 +171,15 @@ class StripePaymentInProgressViewModel(
private fun proceedOneTime(
request: GatewayRequest,
paymentSourceProvider: Single<StripeApi.PaymentSource>,
paymentSourceProvider: PaymentSourceProvider,
nextActionHandler: (StripeApi.Secure3DSAction) -> Single<StripeIntentAccessor>
) {
Log.w(TAG, "Beginning one-time payment pipeline...", true)
val amount = request.fiat
val continuePayment: Single<StripeIntentAccessor> = stripeRepository.continuePayment(amount, request.recipientId, request.level)
val intentAndSource: Single<Pair<StripeIntentAccessor, StripeApi.PaymentSource>> = Single.zip(continuePayment, paymentSourceProvider, ::Pair)
val continuePayment: Single<StripeIntentAccessor> = stripeRepository.continuePayment(amount, request.recipientId, request.level, paymentSourceProvider.paymentSourceType)
val intentAndSource: Single<Pair<StripeIntentAccessor, StripeApi.PaymentSource>> = Single.zip(continuePayment, paymentSourceProvider.paymentSource, ::Pair)
disposables += intentAndSource.flatMapCompletable { (paymentIntent, paymentSource) ->
stripeRepository.confirmPayment(paymentSource, paymentIntent, request.recipientId)
@ -249,6 +256,11 @@ class StripePaymentInProgressViewModel(
)
}
private data class PaymentSourceProvider(
val paymentSourceType: StripePaymentSourceType,
val paymentSource: Single<StripeApi.PaymentSource>
)
class Factory(
private val stripeRepository: StripeRepository,
private val monthlyDonationRepository: MonthlyDonationRepository = MonthlyDonationRepository(ApplicationDependencies.getDonationsService()),

Wyświetl plik

@ -7,6 +7,7 @@ import io.reactivex.rxjava3.subjects.Subject
import org.signal.core.util.logging.Log
import org.signal.donations.StripeDeclineCode
import org.signal.donations.StripeError
import org.signal.donations.StripePaymentSourceType
sealed class DonationError(val source: DonationErrorSource, cause: Throwable) : Exception(cause) {
@ -55,7 +56,7 @@ sealed class DonationError(val source: DonationErrorSource, cause: Throwable) :
/**
* Payment failed by the credit card processor, with a specific reason told to us by Stripe.
*/
class DeclinedError(source: DonationErrorSource, cause: Throwable, val declineCode: StripeDeclineCode) : PaymentSetupError(source, cause)
class DeclinedError(source: DonationErrorSource, cause: Throwable, val declineCode: StripeDeclineCode, val method: StripePaymentSourceType) : PaymentSetupError(source, cause)
}
/**
@ -132,13 +133,13 @@ sealed class DonationError(val source: DonationErrorSource, cause: Throwable) :
* charge has occurred.
*/
@JvmStatic
fun getPaymentSetupError(source: DonationErrorSource, throwable: Throwable): DonationError {
fun getPaymentSetupError(source: DonationErrorSource, throwable: Throwable, method: StripePaymentSourceType): DonationError {
return if (throwable is StripeError.PostError) {
val declineCode: StripeDeclineCode? = throwable.declineCode
val errorCode: String? = throwable.errorCode
when {
declineCode != null -> PaymentSetupError.DeclinedError(source, throwable, declineCode)
declineCode != null -> PaymentSetupError.DeclinedError(source, throwable, declineCode, method)
errorCode != null -> PaymentSetupError.CodedError(source, throwable, errorCode)
else -> PaymentSetupError.GenericError(source, throwable)
}

Wyświetl plik

@ -36,7 +36,7 @@ object DonationErrorDialogs {
return builder.show()
}
open class DialogCallback : DonationErrorParams.Callback<Unit> {
abstract class DialogCallback : DonationErrorParams.Callback<Unit> {
override fun onCancel(context: Context): DonationErrorParams.ErrorAction<Unit>? {
return DonationErrorParams.ErrorAction(
@ -61,6 +61,13 @@ object DonationErrorDialogs {
)
}
override fun onTryCreditCardAgain(context: Context): DonationErrorParams.ErrorAction<Unit>? {
return DonationErrorParams.ErrorAction(
label = R.string.DeclineCode__try,
action = {}
)
}
override fun onLearnMore(context: Context): DonationErrorParams.ErrorAction<Unit>? {
return DonationErrorParams.ErrorAction(
label = R.string.DeclineCode__learn_more,

Wyświetl plik

@ -63,6 +63,8 @@ object DonationErrorNotifications {
)
}
override fun onTryCreditCardAgain(context: Context): DonationErrorParams.ErrorAction<PendingIntent>? = null
override fun onGoToGooglePay(context: Context): DonationErrorParams.ErrorAction<PendingIntent> {
return createAction(
context = context,

Wyświetl plik

@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.components.settings.app.subscription.errors
import android.content.Context
import androidx.annotation.StringRes
import org.signal.donations.StripeDeclineCode
import org.signal.donations.StripePaymentSourceType
import org.thoughtcrime.securesms.R
class DonationErrorParams<V> private constructor(
@ -88,19 +89,78 @@ class DonationErrorParams<V> private constructor(
}
private fun <V> getDeclinedErrorParams(context: Context, declinedError: DonationError.PaymentSetupError.DeclinedError, callback: Callback<V>): DonationErrorParams<V> {
val getStripeDeclineCodePositiveActionParams: (Context, Callback<V>, Int) -> DonationErrorParams<V> = when (declinedError.method) {
StripePaymentSourceType.CREDIT_CARD -> this::getTryCreditCardAgainParams
StripePaymentSourceType.GOOGLE_PAY -> this::getGoToGooglePayParams
}
return when (declinedError.declineCode) {
is StripeDeclineCode.Known -> when (declinedError.declineCode.code) {
StripeDeclineCode.Code.APPROVE_WITH_ID -> getGoToGooglePayParams(context, callback, R.string.DeclineCode__verify_your_payment_method_is_up_to_date_in_google_pay_and_try_again)
StripeDeclineCode.Code.CALL_ISSUER -> getGoToGooglePayParams(context, callback, R.string.DeclineCode__verify_your_payment_method_is_up_to_date_in_google_pay_and_try_again_if_the_problem)
StripeDeclineCode.Code.APPROVE_WITH_ID -> getStripeDeclineCodePositiveActionParams(
context, callback,
when (declinedError.method) {
StripePaymentSourceType.CREDIT_CARD -> R.string.DeclineCode__verify_your_payment_method_is_up_to_date_in_google_pay_and_try_again
StripePaymentSourceType.GOOGLE_PAY -> R.string.DeclineCode__verify_your_card_details_are_correct_and_try_again
}
)
StripeDeclineCode.Code.CALL_ISSUER -> getStripeDeclineCodePositiveActionParams(
context, callback,
when (declinedError.method) {
StripePaymentSourceType.CREDIT_CARD -> R.string.DeclineCode__verify_your_card_details_are_correct_and_try_again_if_the_problem_continues
StripePaymentSourceType.GOOGLE_PAY -> R.string.DeclineCode__verify_your_payment_method_is_up_to_date_in_google_pay_and_try_again_if_the_problem
}
)
StripeDeclineCode.Code.CARD_NOT_SUPPORTED -> getLearnMoreParams(context, callback, R.string.DeclineCode__your_card_does_not_support_this_type_of_purchase)
StripeDeclineCode.Code.EXPIRED_CARD -> getGoToGooglePayParams(context, callback, R.string.DeclineCode__your_card_has_expired)
StripeDeclineCode.Code.INCORRECT_NUMBER -> getGoToGooglePayParams(context, callback, R.string.DeclineCode__your_card_number_is_incorrect)
StripeDeclineCode.Code.INCORRECT_CVC -> getGoToGooglePayParams(context, callback, R.string.DeclineCode__your_cards_cvc_number_is_incorrect)
StripeDeclineCode.Code.EXPIRED_CARD -> getStripeDeclineCodePositiveActionParams(
context, callback,
when (declinedError.method) {
StripePaymentSourceType.CREDIT_CARD -> R.string.DeclineCode__your_card_has_expired_verify_your_card_details
StripePaymentSourceType.GOOGLE_PAY -> R.string.DeclineCode__your_card_has_expired
}
)
StripeDeclineCode.Code.INCORRECT_NUMBER -> getStripeDeclineCodePositiveActionParams(
context, callback,
when (declinedError.method) {
StripePaymentSourceType.CREDIT_CARD -> R.string.DeclineCode__your_card_number_is_incorrect_verify_your_card_details
StripePaymentSourceType.GOOGLE_PAY -> R.string.DeclineCode__your_card_number_is_incorrect
}
)
StripeDeclineCode.Code.INCORRECT_CVC -> getStripeDeclineCodePositiveActionParams(
context, callback,
when (declinedError.method) {
StripePaymentSourceType.CREDIT_CARD -> R.string.DeclineCode__your_cards_cvc_number_is_incorrect_verify_your_card_details
StripePaymentSourceType.GOOGLE_PAY -> R.string.DeclineCode__your_cards_cvc_number_is_incorrect
}
)
StripeDeclineCode.Code.INSUFFICIENT_FUNDS -> getLearnMoreParams(context, callback, R.string.DeclineCode__your_card_does_not_have_sufficient_funds)
StripeDeclineCode.Code.INVALID_CVC -> getGoToGooglePayParams(context, callback, R.string.DeclineCode__your_cards_cvc_number_is_incorrect)
StripeDeclineCode.Code.INVALID_EXPIRY_MONTH -> getGoToGooglePayParams(context, callback, R.string.DeclineCode__the_expiration_month)
StripeDeclineCode.Code.INVALID_EXPIRY_YEAR -> getGoToGooglePayParams(context, callback, R.string.DeclineCode__the_expiration_year)
StripeDeclineCode.Code.INVALID_NUMBER -> getGoToGooglePayParams(context, callback, R.string.DeclineCode__your_card_number_is_incorrect)
StripeDeclineCode.Code.INVALID_CVC -> getStripeDeclineCodePositiveActionParams(
context, callback,
when (declinedError.method) {
StripePaymentSourceType.CREDIT_CARD -> R.string.DeclineCode__your_cards_cvc_number_is_incorrect_verify_your_card_details
StripePaymentSourceType.GOOGLE_PAY -> R.string.DeclineCode__your_cards_cvc_number_is_incorrect
}
)
StripeDeclineCode.Code.INVALID_EXPIRY_MONTH -> getStripeDeclineCodePositiveActionParams(
context, callback,
when (declinedError.method) {
StripePaymentSourceType.CREDIT_CARD -> R.string.DeclineCode__the_expiration_month_on_your_card_is_incorrect
StripePaymentSourceType.GOOGLE_PAY -> R.string.DeclineCode__the_expiration_month
}
)
StripeDeclineCode.Code.INVALID_EXPIRY_YEAR -> getStripeDeclineCodePositiveActionParams(
context, callback,
when (declinedError.method) {
StripePaymentSourceType.CREDIT_CARD -> R.string.DeclineCode__the_expiration_year_on_your_card_is_incorrect
StripePaymentSourceType.GOOGLE_PAY -> R.string.DeclineCode__the_expiration_year
}
)
StripeDeclineCode.Code.INVALID_NUMBER -> getStripeDeclineCodePositiveActionParams(
context, callback,
when (declinedError.method) {
StripePaymentSourceType.CREDIT_CARD -> R.string.DeclineCode__your_card_number_is_incorrect_verify_your_card_details
StripePaymentSourceType.GOOGLE_PAY -> R.string.DeclineCode__your_card_number_is_incorrect
}
)
StripeDeclineCode.Code.ISSUER_NOT_AVAILABLE -> getLearnMoreParams(context, callback, R.string.DeclineCode__try_completing_the_payment_again)
StripeDeclineCode.Code.PROCESSING_ERROR -> getLearnMoreParams(context, callback, R.string.DeclineCode__try_again)
StripeDeclineCode.Code.REENTER_TRANSACTION -> getLearnMoreParams(context, callback, R.string.DeclineCode__try_again)
@ -127,6 +187,15 @@ class DonationErrorParams<V> private constructor(
negativeAction = callback.onCancel(context)
)
}
private fun <V> getTryCreditCardAgainParams(context: Context, callback: Callback<V>, message: Int): DonationErrorParams<V> {
return DonationErrorParams(
title = R.string.DonationsErrors__error_processing_payment,
message = message,
positiveAction = callback.onTryCreditCardAgain(context),
negativeAction = callback.onCancel(context)
)
}
}
interface Callback<V> {
@ -135,5 +204,6 @@ class DonationErrorParams<V> private constructor(
fun onLearnMore(context: Context): ErrorAction<V>?
fun onContactSupport(context: Context): ErrorAction<V>?
fun onGoToGooglePay(context: Context): ErrorAction<V>?
fun onTryCreditCardAgain(context: Context): ErrorAction<V>?
}
}

Wyświetl plik

@ -300,7 +300,8 @@ public class SubscriptionReceiptRequestResponseJob extends BaseJob {
paymentSetupError = new DonationError.PaymentSetupError.DeclinedError(
getErrorSource(),
new Exception(chargeFailure.getMessage()),
declineCode
declineCode,
SignalStore.donationsValues().getSubscriptionPaymentSourceType()
);
} else {
paymentSetupError = new DonationError.PaymentSetupError.CodedError(

Wyświetl plik

@ -6,6 +6,7 @@ import io.reactivex.rxjava3.subjects.BehaviorSubject
import io.reactivex.rxjava3.subjects.Subject
import org.signal.core.util.logging.Log
import org.signal.donations.StripeApi
import org.signal.donations.StripePaymentSourceType
import org.signal.libsignal.zkgroup.InvalidInputException
import org.signal.libsignal.zkgroup.VerificationFailedException
import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialPresentation
@ -95,6 +96,12 @@ internal class DonationsValues internal constructor(store: KeyValueStore) : Sign
* assumed that there is no work to be done.
*/
private const val SUBSCRIPTION_EOP_REDEEMED = "subscription.eop.redeemed"
/**
* Notes the type of payment the user utilized for the latest subscription. This is useful
* in determining which error messaging they should see if something goes wrong.
*/
private const val SUBSCRIPTION_PAYMENT_SOURCE_TYPE = "subscription.payment.source.type"
}
override fun onFirstEverAppLaunch() = Unit
@ -112,7 +119,8 @@ internal class DonationsValues internal constructor(store: KeyValueStore) : Sign
SUBSCRIPTION_CREDENTIAL_RECEIPT,
SUBSCRIPTION_EOP_STARTED_TO_CONVERT,
SUBSCRIPTION_EOP_STARTED_TO_REDEEM,
SUBSCRIPTION_EOP_REDEEMED
SUBSCRIPTION_EOP_REDEEMED,
SUBSCRIPTION_PAYMENT_SOURCE_TYPE
)
private val subscriptionCurrencyPublisher: Subject<Currency> by lazy { BehaviorSubject.createDefault(getSubscriptionCurrency()) }
@ -442,6 +450,14 @@ internal class DonationsValues internal constructor(store: KeyValueStore) : Sign
remove(SUBSCRIPTION_CREDENTIAL_RECEIPT)
}
fun setSubscriptionPaymentSourceType(stripePaymentSourceType: StripePaymentSourceType) {
putString(SUBSCRIPTION_PAYMENT_SOURCE_TYPE, stripePaymentSourceType.code)
}
fun getSubscriptionPaymentSourceType(): StripePaymentSourceType {
return StripePaymentSourceType.fromCode(getString(SUBSCRIPTION_PAYMENT_SOURCE_TYPE, null))
}
var subscriptionEndOfPeriodConversionStarted by longValue(SUBSCRIPTION_EOP_STARTED_TO_CONVERT, 0L)
var subscriptionEndOfPeriodRedemptionStarted by longValue(SUBSCRIPTION_EOP_STARTED_TO_REDEEM, 0L)
var subscriptionEndOfPeriodRedeemed by longValue(SUBSCRIPTION_EOP_REDEEMED, 0L)

Wyświetl plik

@ -4607,6 +4607,8 @@
<string name="DeclineCode__your_card_has_expired">Your card has expired. Update your payment method in Google Pay and try again.</string>
<!-- Stripe decline code go to google pay action label -->
<string name="DeclineCode__go_to_google_pay">Go to Google Pay</string>
<!-- Stripe decline code try credit card again action label -->
<string name="DeclineCode__try">Try again</string>
<!-- Stripe decline code incorrect card number -->
<string name="DeclineCode__your_card_number_is_incorrect">Your card number is incorrect. Update it in Google Pay and try again.</string>
<!-- Stripe decline code incorrect cvc -->
@ -4622,6 +4624,22 @@
<!-- Stripe decline code processing error -->
<string name="DeclineCode__try_again">Try again or contact your bank for more information.</string>
<!-- Credit Card decline code error strings -->
<!-- Stripe decline code approve_with_id for credit cards displayed in a notification or dialog -->
<string name="DeclineCode__verify_your_card_details_are_correct_and_try_again">Verify your card details are correct and try again.</string>
<!-- Stripe decline code call_issuer for credit cards displayed in a notification or dialog -->
<string name="DeclineCode__verify_your_card_details_are_correct_and_try_again_if_the_problem_continues">Verify your card details are correct and try again. If the problem continues, contact your bank.</string>
<!-- Stripe decline code expired_card for credit cards displayed in a notification or dialog -->
<string name="DeclineCode__your_card_has_expired_verify_your_card_details">Your card has expired. Verify your card details are correct and try again.</string>
<!-- Stripe decline code incorrect_cvc and invalid_cvc for credit cards displayed in a notification or dialog -->
<string name="DeclineCode__your_cards_cvc_number_is_incorrect_verify_your_card_details">Your card\'s CVC number is incorrect. Verify your card details are correct and try again.</string>
<!-- Stripe decline code invalid_expiry_month for credit cards displayed in a notification or dialog -->
<string name="DeclineCode__the_expiration_month_on_your_card_is_incorrect">The expiration month on your card is incorrect. Verify your card details are correct and try again.</string>
<!-- Stripe decline code invalid_expiry_year for credit cards displayed in a notification or dialog -->
<string name="DeclineCode__the_expiration_year_on_your_card_is_incorrect">The expiration year on your card is incorrect. Verify your card details are correct and try again.</string>
<!-- Stripe decline code incorrect_number and invalid_number for credit cards displayed in a notification or dialog -->
<string name="DeclineCode__your_card_number_is_incorrect_verify_your_card_details">Your card number is incorrect. Verify your card details are correct and try again.</string>
<!-- Title of create notification profile screen -->
<string name="EditNotificationProfileFragment__name_your_profile">Name your profile</string>
<!-- Hint text for create/edit notification profile name -->

Wyświetl plik

@ -8,6 +8,7 @@ import org.json.JSONObject
class CreditCardPaymentSource(
private val payload: JSONObject
) : StripeApi.PaymentSource {
override val type = StripePaymentSourceType.CREDIT_CARD
override fun parameterize(): JSONObject = payload
override fun getTokenId(): String = parameterize().getString("id")
override fun email(): String? = null

Wyświetl plik

@ -4,6 +4,8 @@ import com.google.android.gms.wallet.PaymentData
import org.json.JSONObject
class GooglePayPaymentSource(private val paymentData: PaymentData) : StripeApi.PaymentSource {
override val type = StripePaymentSourceType.GOOGLE_PAY
override fun parameterize(): JSONObject {
val jsonData = JSONObject(paymentData.toJson())
val paymentMethodJsonData = jsonData.getJSONObject("paymentMethodData")

Wyświetl plik

@ -520,6 +520,7 @@ class StripeApi(
) : Parcelable
interface PaymentSource {
val type: StripePaymentSourceType
fun parameterize(): JSONObject
fun getTokenId(): String
fun email(): String?

Wyświetl plik

@ -0,0 +1,12 @@
package org.signal.donations
enum class StripePaymentSourceType(val code: String) {
CREDIT_CARD("credit_card"),
GOOGLE_PAY("google_pay");
companion object {
fun fromCode(code: String?): StripePaymentSourceType {
return values().firstOrNull { it.code == code } ?: GOOGLE_PAY
}
}
}