Fix donate payment selection when Paypal is not supported

Co-authored-by: ayumi-signal <143036029+ayumi-signal@users.noreply.github.com>
This commit is contained in:
automated-signal
2026-02-11 09:16:50 -06:00
committed by GitHub
parent 651e5427d5
commit fdcea0e9fc
4 changed files with 44 additions and 6 deletions

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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<typeof stripeDonationAmountSchema>;
export const subscriptionConfigurationCurrencyZod = z.object({
minimum: humanDonationAmountSchema,
oneTime: z.record(z.string(), humanDonationAmountSchema.array()),
supportedPaymentMethods: z.array(z.string()),
});
export const oneTimeDonationAmountsZod = z.record(

View File

@@ -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<OneTimeDonationHumanAmounts> {
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<void> {