diff --git a/ts/components/Preferences.dom.stories.tsx b/ts/components/Preferences.dom.stories.tsx index 32cf681987..f63b68160f 100644 --- a/ts/components/Preferences.dom.stories.tsx +++ b/ts/components/Preferences.dom.stories.tsx @@ -139,6 +139,7 @@ const donationAmountsConfig = { 1: [7, 15, 30, 40, 70, 140], 100: [7], }, + supportedPaymentMethods: ['CARD, PAYPAL'], }, jpy: { minimum: 400, @@ -146,6 +147,7 @@ const donationAmountsConfig = { '1': [500, 1000, 2000, 3000, 5000, 10000], '100': [500], }, + supportedPaymentMethods: ['CARD, PAYPAL'], }, usd: { minimum: 3, @@ -153,6 +155,7 @@ const donationAmountsConfig = { 1: [5, 10, 20, 30, 50, 100], 100: [5], }, + supportedPaymentMethods: ['CARD, PAYPAL'], }, ugx: { minimum: 8000, @@ -160,6 +163,7 @@ const donationAmountsConfig = { 1: [15000, 35000, 70000, 100000, 150000, 300000], 100: [15000], }, + supportedPaymentMethods: ['CARD'], }, } as unknown as OneTimeDonationHumanAmounts; diff --git a/ts/components/PreferencesDonateFlow.dom.tsx b/ts/components/PreferencesDonateFlow.dom.tsx index 183266b312..ec8b54766e 100644 --- a/ts/components/PreferencesDonateFlow.dom.tsx +++ b/ts/components/PreferencesDonateFlow.dom.tsx @@ -19,6 +19,7 @@ import { DonationProcessor, donationStateSchema, ONE_TIME_DONATION_CONFIG_ID, + PaymentMethod, } from '../types/Donations.std.js'; import type { CardDetail, @@ -194,18 +195,35 @@ export function PreferencesDonateFlow({ setCurrency(value); }, []); + const isPaymentProcessorStepEnabled = useMemo(() => { + if ( + donationAmountsConfig == null || + donationAmountsConfig[currency] == null + ) { + return false; + } + + return ( + isDonationPaypalEnabled && + donationAmountsConfig[currency].supportedPaymentMethods.includes( + PaymentMethod.Paypal + ) + ); + }, [isDonationPaypalEnabled, donationAmountsConfig, currency]); + const handleAmountPickerResult = useCallback( (result: AmountPickerResult) => { const { currency: pickedCurrency, amount: pickedAmount } = result; setAmount(pickedAmount); setCurrency(pickedCurrency); - if (isDonationPaypalEnabled) { + + if (isPaymentProcessorStepEnabled) { setStep('paymentProcessor'); } else { setStep('stripePaymentDetails'); } }, - [isDonationPaypalEnabled] + [isPaymentProcessorStepEnabled] ); const handleCardFormChanged = useCallback((values: CardFormValues) => { @@ -269,12 +287,12 @@ export function PreferencesDonateFlow({ }, [handleSubmitDonation]); const handleBackFromCardForm = useCallback(() => { - if (isDonationPaypalEnabled) { + if (isPaymentProcessorStepEnabled) { setStep('paymentProcessor'); } else { setStep('amount'); } - }, [isDonationPaypalEnabled]); + }, [isPaymentProcessorStepEnabled]); useEffect(() => { if (!workflow || lastError) { diff --git a/ts/types/Donations.std.ts b/ts/types/Donations.std.ts index 0a168ed75d..c76b2213a2 100644 --- a/ts/types/Donations.std.ts +++ b/ts/types/Donations.std.ts @@ -36,6 +36,11 @@ export enum DonationProcessor { Stripe = 'STRIPE', } +export enum PaymentMethod { + Card = 'CARD', + Paypal = 'PAYPAL', +} + export const donationProcessorSchema = z.nativeEnum(DonationProcessor); export const donationErrorTypeSchema = z.enum([ @@ -262,6 +267,7 @@ export type StripeDonationAmount = z.infer; export const subscriptionConfigurationCurrencyZod = z.object({ minimum: humanDonationAmountSchema, oneTime: z.record(z.string(), humanDonationAmountSchema.array()), + supportedPaymentMethods: z.array(z.string()), }); export const oneTimeDonationAmountsZod = z.record( diff --git a/ts/util/subscriptionConfiguration.preload.ts b/ts/util/subscriptionConfiguration.preload.ts index 0506ecfdec..b027d31783 100644 --- a/ts/util/subscriptionConfiguration.preload.ts +++ b/ts/util/subscriptionConfiguration.preload.ts @@ -1,9 +1,13 @@ // Copyright 2025 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only +import { pickBy } from 'lodash'; import type { SubscriptionConfigurationResultType } from '../textsecure/WebAPI.preload.js'; import { getSubscriptionConfiguration } from '../textsecure/WebAPI.preload.js'; -import type { OneTimeDonationHumanAmounts } from '../types/Donations.std.js'; +import { + PaymentMethod, + type OneTimeDonationHumanAmounts, +} from '../types/Donations.std.js'; import { HOUR } from './durations/index.std.js'; import { isInPast } from './timestamp.std.js'; import { createLogger } from '../logging/log.std.js'; @@ -58,7 +62,13 @@ export function getCachedSubscriptionConfigExpiresAt(): number | undefined { export async function getCachedDonationHumanAmounts(): Promise { const { currencies } = await getCachedSubscriptionConfiguration(); - return currencies; + // pickBy returns a Partial so we need to cast it + return pickBy( + currencies, + ({ supportedPaymentMethods }) => + supportedPaymentMethods.includes(PaymentMethod.Card) || + supportedPaymentMethods.includes(PaymentMethod.Paypal) + ) as unknown as OneTimeDonationHumanAmounts; } export async function maybeHydrateDonationConfigCache(): Promise {