Add SEPA API endpoints.
This commit is contained in:
parent
f5c5a34798
commit
6279149cb8
13 changed files with 140 additions and 36 deletions
|
@ -14,6 +14,7 @@ import java.util.Currency
|
||||||
|
|
||||||
private const val CARD = "CARD"
|
private const val CARD = "CARD"
|
||||||
private const val PAYPAL = "PAYPAL"
|
private const val PAYPAL = "PAYPAL"
|
||||||
|
private const val SEPA_DEBIT = "SEPA_DEBIT"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms the DonationsConfiguration into a Set<FiatMoney> which has been properly filtered
|
* Transforms the DonationsConfiguration into a Set<FiatMoney> which has been properly filtered
|
||||||
|
@ -116,6 +117,7 @@ private fun DonationsConfiguration.getFilteredCurrencies(paymentMethodAvailabili
|
||||||
interface PaymentMethodAvailability {
|
interface PaymentMethodAvailability {
|
||||||
fun isPayPalAvailable(): Boolean
|
fun isPayPalAvailable(): Boolean
|
||||||
fun isGooglePayOrCreditCardAvailable(): Boolean
|
fun isGooglePayOrCreditCardAvailable(): Boolean
|
||||||
|
fun isSEPADebitAvailable(): Boolean
|
||||||
|
|
||||||
fun toSet(): Set<String> {
|
fun toSet(): Set<String> {
|
||||||
val set = mutableSetOf<String>()
|
val set = mutableSetOf<String>()
|
||||||
|
@ -127,6 +129,10 @@ interface PaymentMethodAvailability {
|
||||||
set.add(CARD)
|
set.add(CARD)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isSEPADebitAvailable()) {
|
||||||
|
set.add(SEPA_DEBIT)
|
||||||
|
}
|
||||||
|
|
||||||
return set
|
return set
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,4 +140,5 @@ interface PaymentMethodAvailability {
|
||||||
private object DefaultPaymentMethodAvailability : PaymentMethodAvailability {
|
private object DefaultPaymentMethodAvailability : PaymentMethodAvailability {
|
||||||
override fun isPayPalAvailable(): Boolean = InAppDonations.isPayPalAvailable()
|
override fun isPayPalAvailable(): Boolean = InAppDonations.isPayPalAvailable()
|
||||||
override fun isGooglePayOrCreditCardAvailable(): Boolean = InAppDonations.isCreditCardAvailable() || InAppDonations.isGooglePayAvailable()
|
override fun isGooglePayOrCreditCardAvailable(): Boolean = InAppDonations.isCreditCardAvailable() || InAppDonations.isGooglePayAvailable()
|
||||||
|
override fun isSEPADebitAvailable(): Boolean = InAppDonations.isSEPADebitAvailable()
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,10 +16,11 @@ object InAppDonations {
|
||||||
*
|
*
|
||||||
* - Able to use Credit Cards and is in a region where they are able to be accepted.
|
* - Able to use Credit Cards and is in a region where they are able to be accepted.
|
||||||
* - Able to access Google Play services (and thus possibly able to use Google Pay).
|
* - Able to access Google Play services (and thus possibly able to use Google Pay).
|
||||||
|
* - Able to use SEPA Debit and is in a region where they are able to be accepted.
|
||||||
* - Able to use PayPal and is in a region where it is able to be accepted.
|
* - Able to use PayPal and is in a region where it is able to be accepted.
|
||||||
*/
|
*/
|
||||||
fun hasAtLeastOnePaymentMethodAvailable(): Boolean {
|
fun hasAtLeastOnePaymentMethodAvailable(): Boolean {
|
||||||
return isCreditCardAvailable() || isPayPalAvailable() || isGooglePayAvailable()
|
return isCreditCardAvailable() || isPayPalAvailable() || isGooglePayAvailable() || isSEPADebitAvailable()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isPaymentSourceAvailable(paymentSourceType: PaymentSourceType, donateToSignalType: DonateToSignalType): Boolean {
|
fun isPaymentSourceAvailable(paymentSourceType: PaymentSourceType, donateToSignalType: DonateToSignalType): Boolean {
|
||||||
|
@ -27,6 +28,7 @@ object InAppDonations {
|
||||||
PaymentSourceType.PayPal -> isPayPalAvailableForDonateToSignalType(donateToSignalType)
|
PaymentSourceType.PayPal -> isPayPalAvailableForDonateToSignalType(donateToSignalType)
|
||||||
PaymentSourceType.Stripe.CreditCard -> isCreditCardAvailable()
|
PaymentSourceType.Stripe.CreditCard -> isCreditCardAvailable()
|
||||||
PaymentSourceType.Stripe.GooglePay -> isGooglePayAvailable()
|
PaymentSourceType.Stripe.GooglePay -> isGooglePayAvailable()
|
||||||
|
PaymentSourceType.Stripe.SEPADebit -> isSEPADebitAvailable()
|
||||||
PaymentSourceType.Unknown -> false
|
PaymentSourceType.Unknown -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,4 +60,11 @@ object InAppDonations {
|
||||||
fun isGooglePayAvailable(): Boolean {
|
fun isGooglePayAvailable(): Boolean {
|
||||||
return SignalStore.donationsValues().isGooglePayReady && !LocaleFeatureFlags.isGooglePayDisabled()
|
return SignalStore.donationsValues().isGooglePayReady && !LocaleFeatureFlags.isGooglePayDisabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the user is in a region which supports SEPA Debit transfers, based off local phone number.
|
||||||
|
*/
|
||||||
|
fun isSEPADebitAvailable(): Boolean {
|
||||||
|
return FeatureFlags.sepaDebitDonations()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,9 +90,11 @@ class StripeRepository(activity: Activity) : StripeApi.PaymentIntentFetcher, Str
|
||||||
badgeLevel: Long,
|
badgeLevel: Long,
|
||||||
paymentSourceType: PaymentSourceType
|
paymentSourceType: PaymentSourceType
|
||||||
): Single<StripeIntentAccessor> {
|
): Single<StripeIntentAccessor> {
|
||||||
|
check(paymentSourceType is PaymentSourceType.Stripe)
|
||||||
|
|
||||||
Log.d(TAG, "Creating payment intent for $price...", true)
|
Log.d(TAG, "Creating payment intent for $price...", true)
|
||||||
|
|
||||||
return stripeApi.createPaymentIntent(price, badgeLevel)
|
return stripeApi.createPaymentIntent(price, badgeLevel, paymentSourceType)
|
||||||
.onErrorResumeNext {
|
.onErrorResumeNext {
|
||||||
OneTimeDonationRepository.handleCreatePaymentIntentError(it, badgeRecipient, paymentSourceType)
|
OneTimeDonationRepository.handleCreatePaymentIntentError(it, badgeRecipient, paymentSourceType)
|
||||||
}
|
}
|
||||||
|
@ -110,9 +112,12 @@ class StripeRepository(activity: Activity) : StripeApi.PaymentIntentFetcher, Str
|
||||||
}.subscribeOn(Schedulers.io())
|
}.subscribeOn(Schedulers.io())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createAndConfirmSetupIntent(paymentSource: StripeApi.PaymentSource): Single<StripeApi.Secure3DSAction> {
|
fun createAndConfirmSetupIntent(
|
||||||
|
paymentSource: StripeApi.PaymentSource,
|
||||||
|
paymentSourceType: PaymentSourceType.Stripe
|
||||||
|
): Single<StripeApi.Secure3DSAction> {
|
||||||
Log.d(TAG, "Continuing subscription setup...", true)
|
Log.d(TAG, "Continuing subscription setup...", true)
|
||||||
return stripeApi.createSetupIntent()
|
return stripeApi.createSetupIntent(paymentSourceType)
|
||||||
.flatMap { result ->
|
.flatMap { result ->
|
||||||
Log.d(TAG, "Retrieved SetupIntent, confirming...", true)
|
Log.d(TAG, "Retrieved SetupIntent, confirming...", true)
|
||||||
stripeApi.confirmSetupIntent(paymentSource, result.setupIntent)
|
stripeApi.confirmSetupIntent(paymentSource, result.setupIntent)
|
||||||
|
@ -134,13 +139,13 @@ class StripeRepository(activity: Activity) : StripeApi.PaymentIntentFetcher, Str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun fetchPaymentIntent(price: FiatMoney, level: Long): Single<StripeIntentAccessor> {
|
override fun fetchPaymentIntent(price: FiatMoney, level: Long, sourceType: PaymentSourceType.Stripe): Single<StripeIntentAccessor> {
|
||||||
Log.d(TAG, "Fetching payment intent from Signal service for $price... (Locale.US minimum precision: ${price.minimumUnitPrecisionString})")
|
Log.d(TAG, "Fetching payment intent from Signal service for $price... (Locale.US minimum precision: ${price.minimumUnitPrecisionString})")
|
||||||
return Single
|
return Single
|
||||||
.fromCallable {
|
.fromCallable {
|
||||||
ApplicationDependencies
|
ApplicationDependencies
|
||||||
.getDonationsService()
|
.getDonationsService()
|
||||||
.createDonationIntentWithAmount(price.minimumUnitPrecisionString, price.currency.currencyCode, level)
|
.createDonationIntentWithAmount(price.minimumUnitPrecisionString, price.currency.currencyCode, level, sourceType.paymentMethod)
|
||||||
}
|
}
|
||||||
.flatMap(ServiceResponse<StripeClientSecret>::flattenResult)
|
.flatMap(ServiceResponse<StripeClientSecret>::flattenResult)
|
||||||
.map {
|
.map {
|
||||||
|
@ -159,27 +164,27 @@ class StripeRepository(activity: Activity) : StripeApi.PaymentIntentFetcher, Str
|
||||||
* it means that the PaymentMethod is already tied to a PayPal account. We can retry in this
|
* it means that the PaymentMethod is already tied to a PayPal account. We can retry in this
|
||||||
* situation by simply deleting the old subscriber id on the service and replacing it.
|
* situation by simply deleting the old subscriber id on the service and replacing it.
|
||||||
*/
|
*/
|
||||||
private fun createPaymentMethod(retryOn409: Boolean = true): Single<StripeClientSecret> {
|
private fun createPaymentMethod(paymentSourceType: PaymentSourceType.Stripe, retryOn409: Boolean = true): Single<StripeClientSecret> {
|
||||||
return Single.fromCallable { SignalStore.donationsValues().requireSubscriber() }
|
return Single.fromCallable { SignalStore.donationsValues().requireSubscriber() }
|
||||||
.flatMap {
|
.flatMap {
|
||||||
Single.fromCallable {
|
Single.fromCallable {
|
||||||
ApplicationDependencies
|
ApplicationDependencies
|
||||||
.getDonationsService()
|
.getDonationsService()
|
||||||
.createStripeSubscriptionPaymentMethod(it.subscriberId)
|
.createStripeSubscriptionPaymentMethod(it.subscriberId, paymentSourceType.paymentMethod)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.flatMap { serviceResponse ->
|
.flatMap { serviceResponse ->
|
||||||
if (retryOn409 && serviceResponse.status == 409) {
|
if (retryOn409 && serviceResponse.status == 409) {
|
||||||
monthlyDonationRepository.rotateSubscriberId().andThen(createPaymentMethod(retryOn409 = false))
|
monthlyDonationRepository.rotateSubscriberId().andThen(createPaymentMethod(paymentSourceType, retryOn409 = false))
|
||||||
} else {
|
} else {
|
||||||
serviceResponse.flattenResult()
|
serviceResponse.flattenResult()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun fetchSetupIntent(): Single<StripeIntentAccessor> {
|
override fun fetchSetupIntent(sourceType: PaymentSourceType.Stripe): Single<StripeIntentAccessor> {
|
||||||
Log.d(TAG, "Fetching setup intent from Signal service...")
|
Log.d(TAG, "Fetching setup intent from Signal service...")
|
||||||
return createPaymentMethod()
|
return createPaymentMethod(sourceType)
|
||||||
.map {
|
.map {
|
||||||
StripeIntentAccessor(
|
StripeIntentAccessor(
|
||||||
objectType = StripeIntentAccessor.ObjectType.SETUP_INTENT,
|
objectType = StripeIntentAccessor.ObjectType.SETUP_INTENT,
|
||||||
|
|
|
@ -128,7 +128,10 @@ class StripePaymentInProgressViewModel(
|
||||||
|
|
||||||
private fun proceedMonthly(request: GatewayRequest, paymentSourceProvider: PaymentSourceProvider, nextActionHandler: (StripeApi.Secure3DSAction) -> Single<StripeIntentAccessor>) {
|
private fun proceedMonthly(request: GatewayRequest, paymentSourceProvider: PaymentSourceProvider, nextActionHandler: (StripeApi.Secure3DSAction) -> Single<StripeIntentAccessor>) {
|
||||||
val ensureSubscriberId: Completable = monthlyDonationRepository.ensureSubscriberId()
|
val ensureSubscriberId: Completable = monthlyDonationRepository.ensureSubscriberId()
|
||||||
val createAndConfirmSetupIntent: Single<StripeApi.Secure3DSAction> = paymentSourceProvider.paymentSource.flatMap { stripeRepository.createAndConfirmSetupIntent(it) }
|
val createAndConfirmSetupIntent: Single<StripeApi.Secure3DSAction> = paymentSourceProvider.paymentSource.flatMap {
|
||||||
|
stripeRepository.createAndConfirmSetupIntent(it, paymentSourceProvider.paymentSourceType as PaymentSourceType.Stripe)
|
||||||
|
}
|
||||||
|
|
||||||
val setLevel: Completable = monthlyDonationRepository.setSubscriptionLevel(request.level.toString())
|
val setLevel: Completable = monthlyDonationRepository.setSubscriptionLevel(request.level.toString())
|
||||||
|
|
||||||
Log.d(TAG, "Starting subscription payment pipeline...", true)
|
Log.d(TAG, "Starting subscription payment pipeline...", true)
|
||||||
|
|
|
@ -33,18 +33,21 @@ class DonationErrorParams<V> private constructor(
|
||||||
positiveAction = callback.onOk(context),
|
positiveAction = callback.onOk(context),
|
||||||
negativeAction = null
|
negativeAction = null
|
||||||
)
|
)
|
||||||
|
|
||||||
is DonationError.BadgeRedemptionError.TimeoutWaitingForTokenError -> DonationErrorParams(
|
is DonationError.BadgeRedemptionError.TimeoutWaitingForTokenError -> DonationErrorParams(
|
||||||
title = R.string.DonationsErrors__still_processing,
|
title = R.string.DonationsErrors__still_processing,
|
||||||
message = R.string.DonationsErrors__your_payment_is_still,
|
message = R.string.DonationsErrors__your_payment_is_still,
|
||||||
positiveAction = callback.onOk(context),
|
positiveAction = callback.onOk(context),
|
||||||
negativeAction = null
|
negativeAction = null
|
||||||
)
|
)
|
||||||
|
|
||||||
is DonationError.BadgeRedemptionError.FailedToValidateCredentialError -> DonationErrorParams(
|
is DonationError.BadgeRedemptionError.FailedToValidateCredentialError -> DonationErrorParams(
|
||||||
title = R.string.DonationsErrors__failed_to_validate_badge,
|
title = R.string.DonationsErrors__failed_to_validate_badge,
|
||||||
message = R.string.DonationsErrors__could_not_validate,
|
message = R.string.DonationsErrors__could_not_validate,
|
||||||
positiveAction = callback.onContactSupport(context),
|
positiveAction = callback.onContactSupport(context),
|
||||||
negativeAction = null
|
negativeAction = null
|
||||||
)
|
)
|
||||||
|
|
||||||
is DonationError.BadgeRedemptionError.GenericError -> getGenericRedemptionError(context, throwable, callback)
|
is DonationError.BadgeRedemptionError.GenericError -> getGenericRedemptionError(context, throwable, callback)
|
||||||
else -> DonationErrorParams(
|
else -> DonationErrorParams(
|
||||||
title = R.string.DonationsErrors__couldnt_add_badge,
|
title = R.string.DonationsErrors__couldnt_add_badge,
|
||||||
|
@ -63,6 +66,7 @@ class DonationErrorParams<V> private constructor(
|
||||||
positiveAction = callback.onContactSupport(context),
|
positiveAction = callback.onContactSupport(context),
|
||||||
negativeAction = null
|
negativeAction = null
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> DonationErrorParams(
|
else -> DonationErrorParams(
|
||||||
title = R.string.DonationsErrors__couldnt_add_badge,
|
title = R.string.DonationsErrors__couldnt_add_badge,
|
||||||
message = R.string.DonationsErrors__your_badge_could_not,
|
message = R.string.DonationsErrors__your_badge_could_not,
|
||||||
|
@ -80,6 +84,7 @@ class DonationErrorParams<V> private constructor(
|
||||||
positiveAction = callback.onOk(context),
|
positiveAction = callback.onOk(context),
|
||||||
negativeAction = null
|
negativeAction = null
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> DonationErrorParams(
|
else -> DonationErrorParams(
|
||||||
title = R.string.DonationsErrors__cannot_send_donation,
|
title = R.string.DonationsErrors__cannot_send_donation,
|
||||||
message = R.string.DonationsErrors__this_user_cant_receive_donations_until,
|
message = R.string.DonationsErrors__this_user_cant_receive_donations_until,
|
||||||
|
@ -97,9 +102,14 @@ class DonationErrorParams<V> private constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <V> getStripeDeclinedErrorParams(context: Context, declinedError: DonationError.PaymentSetupError.StripeDeclinedError, callback: Callback<V>): DonationErrorParams<V> {
|
private fun <V> getStripeDeclinedErrorParams(context: Context, declinedError: DonationError.PaymentSetupError.StripeDeclinedError, callback: Callback<V>): DonationErrorParams<V> {
|
||||||
|
fun unexpectedDeclinedError(declinedError: DonationError.PaymentSetupError.StripeDeclinedError): Nothing {
|
||||||
|
error("Unexpected declined error: ${declinedError.declineCode} during ${declinedError.method} processing.")
|
||||||
|
}
|
||||||
|
|
||||||
val getStripeDeclineCodePositiveActionParams: (Context, Callback<V>, Int) -> DonationErrorParams<V> = when (declinedError.method) {
|
val getStripeDeclineCodePositiveActionParams: (Context, Callback<V>, Int) -> DonationErrorParams<V> = when (declinedError.method) {
|
||||||
PaymentSourceType.Stripe.CreditCard -> this::getTryCreditCardAgainParams
|
PaymentSourceType.Stripe.CreditCard -> this::getTryCreditCardAgainParams
|
||||||
PaymentSourceType.Stripe.GooglePay -> this::getGoToGooglePayParams
|
PaymentSourceType.Stripe.GooglePay -> this::getGoToGooglePayParams
|
||||||
|
PaymentSourceType.Stripe.SEPADebit -> error("Not implemented.")
|
||||||
}
|
}
|
||||||
|
|
||||||
return when (declinedError.declineCode) {
|
return when (declinedError.declineCode) {
|
||||||
|
@ -110,16 +120,20 @@ class DonationErrorParams<V> private constructor(
|
||||||
when (declinedError.method) {
|
when (declinedError.method) {
|
||||||
PaymentSourceType.Stripe.CreditCard -> R.string.DeclineCode__verify_your_card_details_are_correct_and_try_again
|
PaymentSourceType.Stripe.CreditCard -> R.string.DeclineCode__verify_your_card_details_are_correct_and_try_again
|
||||||
PaymentSourceType.Stripe.GooglePay -> R.string.DeclineCode__verify_your_payment_method_is_up_to_date_in_google_pay_and_try_again
|
PaymentSourceType.Stripe.GooglePay -> R.string.DeclineCode__verify_your_payment_method_is_up_to_date_in_google_pay_and_try_again
|
||||||
|
PaymentSourceType.Stripe.SEPADebit -> unexpectedDeclinedError(declinedError)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
StripeDeclineCode.Code.CALL_ISSUER -> getStripeDeclineCodePositiveActionParams(
|
StripeDeclineCode.Code.CALL_ISSUER -> getStripeDeclineCodePositiveActionParams(
|
||||||
context,
|
context,
|
||||||
callback,
|
callback,
|
||||||
when (declinedError.method) {
|
when (declinedError.method) {
|
||||||
PaymentSourceType.Stripe.CreditCard -> R.string.DeclineCode__verify_your_card_details_are_correct_and_try_again_if_the_problem_continues
|
PaymentSourceType.Stripe.CreditCard -> R.string.DeclineCode__verify_your_card_details_are_correct_and_try_again_if_the_problem_continues
|
||||||
PaymentSourceType.Stripe.GooglePay -> R.string.DeclineCode__verify_your_payment_method_is_up_to_date_in_google_pay_and_try_again_if_the_problem
|
PaymentSourceType.Stripe.GooglePay -> R.string.DeclineCode__verify_your_payment_method_is_up_to_date_in_google_pay_and_try_again_if_the_problem
|
||||||
|
PaymentSourceType.Stripe.SEPADebit -> unexpectedDeclinedError(declinedError)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
StripeDeclineCode.Code.CARD_NOT_SUPPORTED -> getLearnMoreParams(context, callback, R.string.DeclineCode__your_card_does_not_support_this_type_of_purchase)
|
StripeDeclineCode.Code.CARD_NOT_SUPPORTED -> getLearnMoreParams(context, callback, R.string.DeclineCode__your_card_does_not_support_this_type_of_purchase)
|
||||||
StripeDeclineCode.Code.EXPIRED_CARD -> getStripeDeclineCodePositiveActionParams(
|
StripeDeclineCode.Code.EXPIRED_CARD -> getStripeDeclineCodePositiveActionParams(
|
||||||
context,
|
context,
|
||||||
|
@ -127,24 +141,30 @@ class DonationErrorParams<V> private constructor(
|
||||||
when (declinedError.method) {
|
when (declinedError.method) {
|
||||||
PaymentSourceType.Stripe.CreditCard -> R.string.DeclineCode__your_card_has_expired_verify_your_card_details
|
PaymentSourceType.Stripe.CreditCard -> R.string.DeclineCode__your_card_has_expired_verify_your_card_details
|
||||||
PaymentSourceType.Stripe.GooglePay -> R.string.DeclineCode__your_card_has_expired
|
PaymentSourceType.Stripe.GooglePay -> R.string.DeclineCode__your_card_has_expired
|
||||||
|
PaymentSourceType.Stripe.SEPADebit -> unexpectedDeclinedError(declinedError)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
StripeDeclineCode.Code.INCORRECT_NUMBER -> getStripeDeclineCodePositiveActionParams(
|
StripeDeclineCode.Code.INCORRECT_NUMBER -> getStripeDeclineCodePositiveActionParams(
|
||||||
context,
|
context,
|
||||||
callback,
|
callback,
|
||||||
when (declinedError.method) {
|
when (declinedError.method) {
|
||||||
PaymentSourceType.Stripe.CreditCard -> R.string.DeclineCode__your_card_number_is_incorrect_verify_your_card_details
|
PaymentSourceType.Stripe.CreditCard -> R.string.DeclineCode__your_card_number_is_incorrect_verify_your_card_details
|
||||||
PaymentSourceType.Stripe.GooglePay -> R.string.DeclineCode__your_card_number_is_incorrect
|
PaymentSourceType.Stripe.GooglePay -> R.string.DeclineCode__your_card_number_is_incorrect
|
||||||
|
PaymentSourceType.Stripe.SEPADebit -> unexpectedDeclinedError(declinedError)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
StripeDeclineCode.Code.INCORRECT_CVC -> getStripeDeclineCodePositiveActionParams(
|
StripeDeclineCode.Code.INCORRECT_CVC -> getStripeDeclineCodePositiveActionParams(
|
||||||
context,
|
context,
|
||||||
callback,
|
callback,
|
||||||
when (declinedError.method) {
|
when (declinedError.method) {
|
||||||
PaymentSourceType.Stripe.CreditCard -> R.string.DeclineCode__your_cards_cvc_number_is_incorrect_verify_your_card_details
|
PaymentSourceType.Stripe.CreditCard -> R.string.DeclineCode__your_cards_cvc_number_is_incorrect_verify_your_card_details
|
||||||
PaymentSourceType.Stripe.GooglePay -> R.string.DeclineCode__your_cards_cvc_number_is_incorrect
|
PaymentSourceType.Stripe.GooglePay -> R.string.DeclineCode__your_cards_cvc_number_is_incorrect
|
||||||
|
PaymentSourceType.Stripe.SEPADebit -> unexpectedDeclinedError(declinedError)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
StripeDeclineCode.Code.INSUFFICIENT_FUNDS -> getLearnMoreParams(context, callback, R.string.DeclineCode__your_card_does_not_have_sufficient_funds)
|
StripeDeclineCode.Code.INSUFFICIENT_FUNDS -> getLearnMoreParams(context, callback, R.string.DeclineCode__your_card_does_not_have_sufficient_funds)
|
||||||
StripeDeclineCode.Code.INVALID_CVC -> getStripeDeclineCodePositiveActionParams(
|
StripeDeclineCode.Code.INVALID_CVC -> getStripeDeclineCodePositiveActionParams(
|
||||||
context,
|
context,
|
||||||
|
@ -152,37 +172,46 @@ class DonationErrorParams<V> private constructor(
|
||||||
when (declinedError.method) {
|
when (declinedError.method) {
|
||||||
PaymentSourceType.Stripe.CreditCard -> R.string.DeclineCode__your_cards_cvc_number_is_incorrect_verify_your_card_details
|
PaymentSourceType.Stripe.CreditCard -> R.string.DeclineCode__your_cards_cvc_number_is_incorrect_verify_your_card_details
|
||||||
PaymentSourceType.Stripe.GooglePay -> R.string.DeclineCode__your_cards_cvc_number_is_incorrect
|
PaymentSourceType.Stripe.GooglePay -> R.string.DeclineCode__your_cards_cvc_number_is_incorrect
|
||||||
|
PaymentSourceType.Stripe.SEPADebit -> unexpectedDeclinedError(declinedError)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
StripeDeclineCode.Code.INVALID_EXPIRY_MONTH -> getStripeDeclineCodePositiveActionParams(
|
StripeDeclineCode.Code.INVALID_EXPIRY_MONTH -> getStripeDeclineCodePositiveActionParams(
|
||||||
context,
|
context,
|
||||||
callback,
|
callback,
|
||||||
when (declinedError.method) {
|
when (declinedError.method) {
|
||||||
PaymentSourceType.Stripe.CreditCard -> R.string.DeclineCode__the_expiration_month_on_your_card_is_incorrect
|
PaymentSourceType.Stripe.CreditCard -> R.string.DeclineCode__the_expiration_month_on_your_card_is_incorrect
|
||||||
PaymentSourceType.Stripe.GooglePay -> R.string.DeclineCode__the_expiration_month
|
PaymentSourceType.Stripe.GooglePay -> R.string.DeclineCode__the_expiration_month
|
||||||
|
PaymentSourceType.Stripe.SEPADebit -> unexpectedDeclinedError(declinedError)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
StripeDeclineCode.Code.INVALID_EXPIRY_YEAR -> getStripeDeclineCodePositiveActionParams(
|
StripeDeclineCode.Code.INVALID_EXPIRY_YEAR -> getStripeDeclineCodePositiveActionParams(
|
||||||
context,
|
context,
|
||||||
callback,
|
callback,
|
||||||
when (declinedError.method) {
|
when (declinedError.method) {
|
||||||
PaymentSourceType.Stripe.CreditCard -> R.string.DeclineCode__the_expiration_year_on_your_card_is_incorrect
|
PaymentSourceType.Stripe.CreditCard -> R.string.DeclineCode__the_expiration_year_on_your_card_is_incorrect
|
||||||
PaymentSourceType.Stripe.GooglePay -> R.string.DeclineCode__the_expiration_year
|
PaymentSourceType.Stripe.GooglePay -> R.string.DeclineCode__the_expiration_year
|
||||||
|
PaymentSourceType.Stripe.SEPADebit -> unexpectedDeclinedError(declinedError)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
StripeDeclineCode.Code.INVALID_NUMBER -> getStripeDeclineCodePositiveActionParams(
|
StripeDeclineCode.Code.INVALID_NUMBER -> getStripeDeclineCodePositiveActionParams(
|
||||||
context,
|
context,
|
||||||
callback,
|
callback,
|
||||||
when (declinedError.method) {
|
when (declinedError.method) {
|
||||||
PaymentSourceType.Stripe.CreditCard -> R.string.DeclineCode__your_card_number_is_incorrect_verify_your_card_details
|
PaymentSourceType.Stripe.CreditCard -> R.string.DeclineCode__your_card_number_is_incorrect_verify_your_card_details
|
||||||
PaymentSourceType.Stripe.GooglePay -> R.string.DeclineCode__your_card_number_is_incorrect
|
PaymentSourceType.Stripe.GooglePay -> R.string.DeclineCode__your_card_number_is_incorrect
|
||||||
|
PaymentSourceType.Stripe.SEPADebit -> unexpectedDeclinedError(declinedError)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
StripeDeclineCode.Code.ISSUER_NOT_AVAILABLE -> getLearnMoreParams(context, callback, R.string.DeclineCode__try_completing_the_payment_again)
|
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.PROCESSING_ERROR -> getLearnMoreParams(context, callback, R.string.DeclineCode__try_again)
|
||||||
StripeDeclineCode.Code.REENTER_TRANSACTION -> getLearnMoreParams(context, callback, R.string.DeclineCode__try_again)
|
StripeDeclineCode.Code.REENTER_TRANSACTION -> getLearnMoreParams(context, callback, R.string.DeclineCode__try_again)
|
||||||
else -> getLearnMoreParams(context, callback, R.string.DeclineCode__try_another_payment_method_or_contact_your_bank)
|
else -> getLearnMoreParams(context, callback, R.string.DeclineCode__try_another_payment_method_or_contact_your_bank)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> getLearnMoreParams(context, callback, R.string.DeclineCode__try_another_payment_method_or_contact_your_bank)
|
else -> getLearnMoreParams(context, callback, R.string.DeclineCode__try_another_payment_method_or_contact_your_bank)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,6 +117,7 @@ public final class FeatureFlags {
|
||||||
public static final String INSTANT_VIDEO_PLAYBACK = "android.instantVideoPlayback";
|
public static final String INSTANT_VIDEO_PLAYBACK = "android.instantVideoPlayback";
|
||||||
private static final String CONVERSATION_ITEM_V2_TEXT = "android.conversationItemV2.text.4";
|
private static final String CONVERSATION_ITEM_V2_TEXT = "android.conversationItemV2.text.4";
|
||||||
public static final String CRASH_PROMPT_CONFIG = "android.crashPromptConfig";
|
public static final String CRASH_PROMPT_CONFIG = "android.crashPromptConfig";
|
||||||
|
private static final String SEPA_DEBIT_DONATIONS = "android.sepa.debit.donations";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We will only store remote values for flags in this set. If you want a flag to be controllable
|
* We will only store remote values for flags in this set. If you want a flag to be controllable
|
||||||
|
@ -190,7 +191,8 @@ public final class FeatureFlags {
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final Set<String> NOT_REMOTE_CAPABLE = SetUtil.newHashSet(
|
static final Set<String> NOT_REMOTE_CAPABLE = SetUtil.newHashSet(
|
||||||
PHONE_NUMBER_PRIVACY
|
PHONE_NUMBER_PRIVACY,
|
||||||
|
SEPA_DEBIT_DONATIONS
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -680,6 +682,13 @@ public final class FeatureFlags {
|
||||||
return getString(CRASH_PROMPT_CONFIG, "");
|
return getString(CRASH_PROMPT_CONFIG, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not SEPA debit payments for donations are enabled.
|
||||||
|
* WARNING: This feature is under heavy development and is *not* ready for wider use.
|
||||||
|
*/
|
||||||
|
public static boolean sepaDebitDonations() {
|
||||||
|
return getBoolean(SEPA_DEBIT_DONATIONS, Environment.IS_STAGING);
|
||||||
|
}
|
||||||
|
|
||||||
/** Only for rendering debug info. */
|
/** Only for rendering debug info. */
|
||||||
public static synchronized @NonNull Map<String, Object> getMemoryValues() {
|
public static synchronized @NonNull Map<String, Object> getMemoryValues() {
|
||||||
|
|
|
@ -212,15 +212,18 @@ class DonationsConfigurationExtensionsKtTest {
|
||||||
private object AllPaymentMethodsAvailability : PaymentMethodAvailability {
|
private object AllPaymentMethodsAvailability : PaymentMethodAvailability {
|
||||||
override fun isPayPalAvailable(): Boolean = true
|
override fun isPayPalAvailable(): Boolean = true
|
||||||
override fun isGooglePayOrCreditCardAvailable(): Boolean = true
|
override fun isGooglePayOrCreditCardAvailable(): Boolean = true
|
||||||
|
override fun isSEPADebitAvailable(): Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private object PayPalOnly : PaymentMethodAvailability {
|
private object PayPalOnly : PaymentMethodAvailability {
|
||||||
override fun isPayPalAvailable(): Boolean = true
|
override fun isPayPalAvailable(): Boolean = true
|
||||||
override fun isGooglePayOrCreditCardAvailable(): Boolean = false
|
override fun isGooglePayOrCreditCardAvailable(): Boolean = false
|
||||||
|
override fun isSEPADebitAvailable(): Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private object CardOnly : PaymentMethodAvailability {
|
private object CardOnly : PaymentMethodAvailability {
|
||||||
override fun isPayPalAvailable(): Boolean = false
|
override fun isPayPalAvailable(): Boolean = false
|
||||||
override fun isGooglePayOrCreditCardAvailable(): Boolean = true
|
override fun isGooglePayOrCreditCardAvailable(): Boolean = true
|
||||||
|
override fun isSEPADebitAvailable(): Boolean = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,16 +11,18 @@ sealed class PaymentSourceType {
|
||||||
override val code: String = Codes.PAY_PAL.code
|
override val code: String = Codes.PAY_PAL.code
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class Stripe(override val code: String) : PaymentSourceType() {
|
sealed class Stripe(override val code: String, val paymentMethod: String) : PaymentSourceType() {
|
||||||
object CreditCard : Stripe(Codes.CREDIT_CARD.code)
|
object CreditCard : Stripe(Codes.CREDIT_CARD.code, "CARD")
|
||||||
object GooglePay : Stripe(Codes.GOOGLE_PAY.code)
|
object GooglePay : Stripe(Codes.GOOGLE_PAY.code, "CARD")
|
||||||
|
object SEPADebit : Stripe(Codes.SEPA_DEBIT.code, "SEPA_DEBIT")
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum class Codes(val code: String) {
|
private enum class Codes(val code: String) {
|
||||||
UNKNOWN("unknown"),
|
UNKNOWN("unknown"),
|
||||||
PAY_PAL("paypal"),
|
PAY_PAL("paypal"),
|
||||||
CREDIT_CARD("credit_card"),
|
CREDIT_CARD("credit_card"),
|
||||||
GOOGLE_PAY("google_pay")
|
GOOGLE_PAY("google_pay"),
|
||||||
|
SEPA_DEBIT("sepa_debit")
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -30,6 +32,7 @@ sealed class PaymentSourceType {
|
||||||
Codes.PAY_PAL -> PayPal
|
Codes.PAY_PAL -> PayPal
|
||||||
Codes.CREDIT_CARD -> Stripe.CreditCard
|
Codes.CREDIT_CARD -> Stripe.CreditCard
|
||||||
Codes.GOOGLE_PAY -> Stripe.GooglePay
|
Codes.GOOGLE_PAY -> Stripe.GooglePay
|
||||||
|
Codes.SEPA_DEBIT -> Stripe.SEPADebit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,9 +59,9 @@ class StripeApi(
|
||||||
data class Failure(val reason: Throwable) : CreatePaymentSourceFromCardDataResult()
|
data class Failure(val reason: Throwable) : CreatePaymentSourceFromCardDataResult()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createSetupIntent(): Single<CreateSetupIntentResult> {
|
fun createSetupIntent(sourceType: PaymentSourceType.Stripe): Single<CreateSetupIntentResult> {
|
||||||
return setupIntentHelper
|
return setupIntentHelper
|
||||||
.fetchSetupIntent()
|
.fetchSetupIntent(sourceType)
|
||||||
.map { CreateSetupIntentResult(it) }
|
.map { CreateSetupIntentResult(it) }
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ class StripeApi(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createPaymentIntent(price: FiatMoney, level: Long): Single<CreatePaymentIntentResult> {
|
fun createPaymentIntent(price: FiatMoney, level: Long, sourceType: PaymentSourceType.Stripe): Single<CreatePaymentIntentResult> {
|
||||||
@Suppress("CascadeIf")
|
@Suppress("CascadeIf")
|
||||||
return if (Validation.isAmountTooSmall(price)) {
|
return if (Validation.isAmountTooSmall(price)) {
|
||||||
Single.just(CreatePaymentIntentResult.AmountIsTooSmall(price))
|
Single.just(CreatePaymentIntentResult.AmountIsTooSmall(price))
|
||||||
|
@ -95,7 +95,7 @@ class StripeApi(
|
||||||
Single.just<CreatePaymentIntentResult>(CreatePaymentIntentResult.CurrencyIsNotSupported(price.currency.currencyCode))
|
Single.just<CreatePaymentIntentResult>(CreatePaymentIntentResult.CurrencyIsNotSupported(price.currency.currencyCode))
|
||||||
} else {
|
} else {
|
||||||
paymentIntentFetcher
|
paymentIntentFetcher
|
||||||
.fetchPaymentIntent(price, level)
|
.fetchPaymentIntent(price, level, sourceType)
|
||||||
.map<CreatePaymentIntentResult> { CreatePaymentIntentResult.Success(it) }
|
.map<CreatePaymentIntentResult> { CreatePaymentIntentResult.Success(it) }
|
||||||
}.subscribeOn(Schedulers.io())
|
}.subscribeOn(Schedulers.io())
|
||||||
}
|
}
|
||||||
|
@ -513,12 +513,15 @@ class StripeApi(
|
||||||
interface PaymentIntentFetcher {
|
interface PaymentIntentFetcher {
|
||||||
fun fetchPaymentIntent(
|
fun fetchPaymentIntent(
|
||||||
price: FiatMoney,
|
price: FiatMoney,
|
||||||
level: Long
|
level: Long,
|
||||||
|
sourceType: PaymentSourceType.Stripe
|
||||||
): Single<StripeIntentAccessor>
|
): Single<StripeIntentAccessor>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SetupIntentHelper {
|
interface SetupIntentHelper {
|
||||||
fun fetchSetupIntent(): Single<StripeIntentAccessor>
|
fun fetchSetupIntent(
|
||||||
|
sourceType: PaymentSourceType.Stripe
|
||||||
|
): Single<StripeIntentAccessor>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
|
|
|
@ -91,8 +91,8 @@ public class DonationsService {
|
||||||
* @param currencyCode The currency code for the amount
|
* @param currencyCode The currency code for the amount
|
||||||
* @return A ServiceResponse containing a DonationIntentResult with details given to us by the payment gateway.
|
* @return A ServiceResponse containing a DonationIntentResult with details given to us by the payment gateway.
|
||||||
*/
|
*/
|
||||||
public ServiceResponse<StripeClientSecret> createDonationIntentWithAmount(String amount, String currencyCode, long level) {
|
public ServiceResponse<StripeClientSecret> createDonationIntentWithAmount(String amount, String currencyCode, long level, String paymentMethod) {
|
||||||
return wrapInServiceResponse(() -> new Pair<>(pushServiceSocket.createStripeOneTimePaymentIntent(currencyCode, Long.parseLong(amount), level), 200));
|
return wrapInServiceResponse(() -> new Pair<>(pushServiceSocket.createStripeOneTimePaymentIntent(currencyCode, paymentMethod, Long.parseLong(amount), level), 200));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -205,9 +205,9 @@ public class DonationsService {
|
||||||
* @return Client secret for a SetupIntent. It should not be used with the PaymentIntent stripe APIs
|
* @return Client secret for a SetupIntent. It should not be used with the PaymentIntent stripe APIs
|
||||||
* but instead with the SetupIntent stripe APIs.
|
* but instead with the SetupIntent stripe APIs.
|
||||||
*/
|
*/
|
||||||
public ServiceResponse<StripeClientSecret> createStripeSubscriptionPaymentMethod(SubscriberId subscriberId) {
|
public ServiceResponse<StripeClientSecret> createStripeSubscriptionPaymentMethod(SubscriberId subscriberId, String type) {
|
||||||
return wrapInServiceResponse(() -> {
|
return wrapInServiceResponse(() -> {
|
||||||
StripeClientSecret clientSecret = pushServiceSocket.createStripeSubscriptionPaymentMethod(subscriberId.serialize());
|
StripeClientSecret clientSecret = pushServiceSocket.createStripeSubscriptionPaymentMethod(subscriberId.serialize(), type);
|
||||||
return new Pair<>(clientSecret, 200);
|
return new Pair<>(clientSecret, 200);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.signalservice.internal.push
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized bank transfer mandate.
|
||||||
|
*/
|
||||||
|
class BankMandate @JsonCreator constructor(@JsonProperty("mandate") mandate: String)
|
|
@ -277,7 +277,7 @@ public class PushServiceSocket {
|
||||||
|
|
||||||
private static final String UPDATE_SUBSCRIPTION_LEVEL = "/v1/subscription/%s/level/%s/%s/%s";
|
private static final String UPDATE_SUBSCRIPTION_LEVEL = "/v1/subscription/%s/level/%s/%s/%s";
|
||||||
private static final String SUBSCRIPTION = "/v1/subscription/%s";
|
private static final String SUBSCRIPTION = "/v1/subscription/%s";
|
||||||
private static final String CREATE_STRIPE_SUBSCRIPTION_PAYMENT_METHOD = "/v1/subscription/%s/create_payment_method";
|
private static final String CREATE_STRIPE_SUBSCRIPTION_PAYMENT_METHOD = "/v1/subscription/%s/create_payment_method?type=%s";
|
||||||
private static final String CREATE_PAYPAL_SUBSCRIPTION_PAYMENT_METHOD = "/v1/subscription/%s/create_payment_method/paypal";
|
private static final String CREATE_PAYPAL_SUBSCRIPTION_PAYMENT_METHOD = "/v1/subscription/%s/create_payment_method/paypal";
|
||||||
private static final String DEFAULT_STRIPE_SUBSCRIPTION_PAYMENT_METHOD = "/v1/subscription/%s/default_payment_method/stripe/%s";
|
private static final String DEFAULT_STRIPE_SUBSCRIPTION_PAYMENT_METHOD = "/v1/subscription/%s/default_payment_method/stripe/%s";
|
||||||
private static final String DEFAULT_PAYPAL_SUBSCRIPTION_PAYMENT_METHOD = "/v1/subscription/%s/default_payment_method/braintree/%s";
|
private static final String DEFAULT_PAYPAL_SUBSCRIPTION_PAYMENT_METHOD = "/v1/subscription/%s/default_payment_method/braintree/%s";
|
||||||
|
@ -287,6 +287,7 @@ public class PushServiceSocket {
|
||||||
private static final String CONFIRM_PAYPAL_ONE_TIME_PAYMENT_INTENT = "/v1/subscription/boost/paypal/confirm";
|
private static final String CONFIRM_PAYPAL_ONE_TIME_PAYMENT_INTENT = "/v1/subscription/boost/paypal/confirm";
|
||||||
private static final String BOOST_RECEIPT_CREDENTIALS = "/v1/subscription/boost/receipt_credentials";
|
private static final String BOOST_RECEIPT_CREDENTIALS = "/v1/subscription/boost/receipt_credentials";
|
||||||
private static final String DONATIONS_CONFIGURATION = "/v1/subscription/configuration";
|
private static final String DONATIONS_CONFIGURATION = "/v1/subscription/configuration";
|
||||||
|
private static final String BANK_MANDATE = "/v1/subscription/bank_mandate/%s";
|
||||||
|
|
||||||
private static final String VERIFICATION_SESSION_PATH = "/v1/verification/session";
|
private static final String VERIFICATION_SESSION_PATH = "/v1/verification/session";
|
||||||
private static final String VERIFICATION_CODE_PATH = "/v1/verification/session/%s/code";
|
private static final String VERIFICATION_CODE_PATH = "/v1/verification/session/%s/code";
|
||||||
|
@ -1139,8 +1140,8 @@ public class PushServiceSocket {
|
||||||
makeServiceRequest(DONATION_REDEEM_RECEIPT, "POST", payload);
|
makeServiceRequest(DONATION_REDEEM_RECEIPT, "POST", payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StripeClientSecret createStripeOneTimePaymentIntent(String currencyCode, long amount, long level) throws IOException {
|
public StripeClientSecret createStripeOneTimePaymentIntent(String currencyCode, String paymentMethod, long amount, long level) throws IOException {
|
||||||
String payload = JsonUtil.toJson(new StripeOneTimePaymentIntentPayload(amount, currencyCode, level));
|
String payload = JsonUtil.toJson(new StripeOneTimePaymentIntentPayload(amount, currencyCode, level, paymentMethod));
|
||||||
String result = makeServiceRequestWithoutAuthentication(CREATE_STRIPE_ONE_TIME_PAYMENT_INTENT, "POST", payload);
|
String result = makeServiceRequestWithoutAuthentication(CREATE_STRIPE_ONE_TIME_PAYMENT_INTENT, "POST", payload);
|
||||||
return JsonUtil.fromJsonResponse(result, StripeClientSecret.class);
|
return JsonUtil.fromJsonResponse(result, StripeClientSecret.class);
|
||||||
}
|
}
|
||||||
|
@ -1196,6 +1197,17 @@ public class PushServiceSocket {
|
||||||
return JsonUtil.fromJson(result, DonationsConfiguration.class);
|
return JsonUtil.fromJson(result, DonationsConfiguration.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bankTransferType Valid values for bankTransferType are {SEPA_DEBIT}.
|
||||||
|
* @return localized bank mandate text for the given bankTransferType.
|
||||||
|
*/
|
||||||
|
public BankMandate getBankMandate(Locale locale, String bankTransferType) throws IOException {
|
||||||
|
Map<String, String> headers = Collections.singletonMap("Accept-Language", locale.getLanguage() + "-" + locale.getCountry());
|
||||||
|
String result = makeServiceRequestWithoutAuthentication(String.format(BANK_MANDATE, bankTransferType), "GET", null, headers, NO_HANDLER);
|
||||||
|
|
||||||
|
return JsonUtil.fromJson(result, BankMandate.class);
|
||||||
|
}
|
||||||
|
|
||||||
public void updateSubscriptionLevel(String subscriberId, String level, String currencyCode, String idempotencyKey) throws IOException {
|
public void updateSubscriptionLevel(String subscriberId, String level, String currencyCode, String idempotencyKey) throws IOException {
|
||||||
makeServiceRequestWithoutAuthentication(String.format(UPDATE_SUBSCRIPTION_LEVEL, subscriberId, level, currencyCode, idempotencyKey), "PUT", "");
|
makeServiceRequestWithoutAuthentication(String.format(UPDATE_SUBSCRIPTION_LEVEL, subscriberId, level, currencyCode, idempotencyKey), "PUT", "");
|
||||||
}
|
}
|
||||||
|
@ -1213,8 +1225,11 @@ public class PushServiceSocket {
|
||||||
makeServiceRequestWithoutAuthentication(String.format(SUBSCRIPTION, subscriberId), "DELETE", null);
|
makeServiceRequestWithoutAuthentication(String.format(SUBSCRIPTION, subscriberId), "DELETE", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StripeClientSecret createStripeSubscriptionPaymentMethod(String subscriberId) throws IOException {
|
/**
|
||||||
String response = makeServiceRequestWithoutAuthentication(String.format(CREATE_STRIPE_SUBSCRIPTION_PAYMENT_METHOD, subscriberId), "POST", "");
|
* @param type One of CARD or SEPA_DEBIT
|
||||||
|
*/
|
||||||
|
public StripeClientSecret createStripeSubscriptionPaymentMethod(String subscriberId, String type) throws IOException {
|
||||||
|
String response = makeServiceRequestWithoutAuthentication(String.format(CREATE_STRIPE_SUBSCRIPTION_PAYMENT_METHOD, subscriberId, type), "POST", "");
|
||||||
return JsonUtil.fromJson(response, StripeClientSecret.class);
|
return JsonUtil.fromJson(response, StripeClientSecret.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,13 @@ class StripeOneTimePaymentIntentPayload {
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private long level;
|
private long level;
|
||||||
|
|
||||||
public StripeOneTimePaymentIntentPayload(long amount, String currency, long level) {
|
@JsonProperty
|
||||||
this.amount = amount;
|
private String paymentMethod;
|
||||||
this.currency = currency;
|
|
||||||
this.level = level;
|
public StripeOneTimePaymentIntentPayload(long amount, String currency, long level, String paymentMethod) {
|
||||||
|
this.amount = amount;
|
||||||
|
this.currency = currency;
|
||||||
|
this.level = level;
|
||||||
|
this.paymentMethod = paymentMethod;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue