From 2fcaedced37d4b3207ec8d2fb84c0529508beb99 Mon Sep 17 00:00:00 2001 From: andrewHEguardian <114918544+andrewHEguardian@users.noreply.github.com> Date: Thu, 2 Jan 2025 12:43:38 +0000 Subject: [PATCH 1/4] create lazy paymentsection component --- .../components/checkoutComponent.tsx | 193 +++-------------- .../components/paymentSection.tsx | 201 ++++++++++++++++++ 2 files changed, 233 insertions(+), 161 deletions(-) create mode 100644 support-frontend/assets/pages/[countryGroupId]/components/paymentSection.tsx diff --git a/support-frontend/assets/pages/[countryGroupId]/components/checkoutComponent.tsx b/support-frontend/assets/pages/[countryGroupId]/components/checkoutComponent.tsx index b378978e73..fe3587cac4 100644 --- a/support-frontend/assets/pages/[countryGroupId]/components/checkoutComponent.tsx +++ b/support-frontend/assets/pages/[countryGroupId]/components/checkoutComponent.tsx @@ -1,12 +1,6 @@ import { css } from '@emotion/react'; import { palette, space } from '@guardian/source/foundations'; -import { - Checkbox, - Label, - Radio, - RadioGroup, - TextInput, -} from '@guardian/source/react-components'; +import { Checkbox, Label, TextInput } from '@guardian/source/react-components'; import { Divider, ErrorSummary, @@ -19,9 +13,8 @@ import { useStripe, } from '@stripe/react-stripe-js'; import type { ExpressPaymentType } from '@stripe/stripe-js'; -import { useEffect, useRef, useState } from 'react'; +import { lazy, Suspense, useEffect, useRef, useState } from 'react'; import { Box, BoxContents } from 'components/checkoutBox/checkoutBox'; -import DirectDebitForm from 'components/directDebit/directDebitForm/directDebitForm'; import { LoadingOverlay } from 'components/loadingOverlay/loadingOverlay'; import { ContributionsOrderSummary } from 'components/orderSummary/contributionsOrderSummary'; import { @@ -29,13 +22,9 @@ import { getTermsStartDateTier3, } from 'components/orderSummary/contributionsOrderSummaryContainer'; import { DefaultPaymentButton } from 'components/paymentButton/defaultPaymentButton'; -import { paymentMethodData } from 'components/paymentMethodSelector/paymentMethodData'; import { PayPalButton } from 'components/payPalPaymentButton/payPalButton'; import { StateSelect } from 'components/personalDetails/stateSelect'; -import { Recaptcha } from 'components/recaptcha/recaptcha'; -import { SecureTransactionIndicator } from 'components/secureTransactionIndicator/secureTransactionIndicator'; import Signout from 'components/signout/signout'; -import { StripeCardForm } from 'components/stripeCardForm/stripeCardForm'; import { AddressFields } from 'components/subscriptionCheckouts/address/addressFields'; import type { PostcodeFinderResult } from 'components/subscriptionCheckouts/address/postcodeLookup'; import { findAddressesForPostcode } from 'components/subscriptionCheckouts/address/postcodeLookup'; @@ -113,10 +102,7 @@ import { paypalOneClickCheckout, setupPayPalPayment, } from '../checkout/helpers/paypal'; -import { - stripeCreateSetupIntentPrb, - stripeCreateSetupIntentRecaptcha, -} from '../checkout/helpers/stripe'; +import { stripeCreateSetupIntentPrb } from '../checkout/helpers/stripe'; import { doesNotContainExtendedEmojiOrLeadingSpace, preventDefaultValidityMessage, @@ -124,16 +110,11 @@ import { import { BackButton } from './backButton'; import { CheckoutLayout } from './checkoutLayout'; import { FormSection, Legend, shorterBoxMargin } from './form'; -import { - checkedRadioLabelColour, - defaultRadioLabelColour, - paymentMethodBody, - PaymentMethodRadio, - PaymentMethodSelector, -} from './paymentMethod'; import { retryPaymentStatus } from './retryPaymentStatus'; import { setThankYouOrder, unsetThankYouOrder } from './thankYouComponent'; +const PaymentSection = lazy(() => import('./paymentSection')); + /** * We have not added StripeExpressCheckoutElement to the old PaymentMethod * as it is heavily coupled through the code base and would require adding @@ -1141,143 +1122,33 @@ export function CheckoutComponent({ )} - - - {productDescription.deliverableTo ? '3' : '2'}. Payment method - - - - - {validPaymentMethods.map((validPaymentMethod) => { - const selected = paymentMethod === validPaymentMethod; - const { label, icon } = paymentMethodData[validPaymentMethod]; - return ( - - - - {label} -
{icon}
- - } - name="paymentMethod" - value={validPaymentMethod} - cssOverrides={ - selected - ? checkedRadioLabelColour - : defaultRadioLabelColour - } - onChange={() => { - setPaymentMethod(validPaymentMethod); - setPaymentMethodError(undefined); - // Track payment method selection with QM - sendEventPaymentMethodSelected(validPaymentMethod); - }} - /> -
- {validPaymentMethod === 'Stripe' && selected && ( -
- - { - // no-op - }} - onExpiryChange={() => { - // no-op - }} - onCvcChange={() => { - // no-op - }} - errors={{}} - recaptcha={ - { - setStripeClientSecretInProgress(true); - setRecaptchaToken(token); - void stripeCreateSetupIntentRecaptcha( - isTestUser, - stripePublicKey, - token, - ).then((client_secret) => { - setStripeClientSecret(client_secret); - setStripeClientSecretInProgress(false); - }); - }} - onRecaptchaExpired={() => { - setRecaptchaToken(undefined); - }} - /> - } - /> -
- )} - - {validPaymentMethod === 'DirectDebit' && selected && ( -
- { - setAccountHolderName(name); - }} - updateAccountNumber={(number: string) => { - setAccountNumber(number); - }} - updateSortCode={(sortCode: string) => { - setSortCode(sortCode); - }} - updateAccountHolderConfirmation={( - confirmation: boolean, - ) => { - setAccountHolderConfirmation(confirmation); - }} - recaptcha={ - { - setRecaptchaToken(token); - }} - onRecaptchaExpired={() => { - setRecaptchaToken(undefined); - }} - /> - } - formError={''} - errors={{}} - /> -
- )} -
- ); - })} -
-
+ Loading...}> + + void; + setStripeClientSecret: (clientSecret: string) => void; + setStripeClientSecretInProgress: (inProgress: boolean) => void; + recaptchaToken: string | undefined; + setRecaptchaToken: (token: string | undefined) => void; + accountHolderName: string; + setAccountHolderName: (name: string) => void; + accountNumber: string; + setAccountNumber: (number: string) => void; + sortCode: string; + setSortCode: (sortCode: string) => void; + accountHolderConfirmation: boolean; + setAccountHolderConfirmation: (confirmation: boolean) => void; + paymentMethod: PaymentMethod | 'StripeExpressCheckoutElement' | undefined; + setPaymentMethod: (paymentMethod: PaymentMethod) => void; + sectionNumber: number; + stripePublicKey: string; + isTestUser: boolean; + countryGroupId: CountryGroupId; +}; + +export default function PaymentSection({ + validPaymentMethods, + paymentMethodError, + setPaymentMethodError, + setStripeClientSecret, + setStripeClientSecretInProgress, + recaptchaToken, + setRecaptchaToken, + accountHolderName, + setAccountHolderName, + accountNumber, + setAccountNumber, + sortCode, + setSortCode, + accountHolderConfirmation, + setAccountHolderConfirmation, + paymentMethod, + setPaymentMethod, + sectionNumber, + stripePublicKey, + isTestUser, + countryGroupId, +}: PaymentSectionProps) { + return ( + + + {sectionNumber}. Payment method + + + + + {validPaymentMethods.map((validPaymentMethod) => { + const selected = paymentMethod === validPaymentMethod; + const { label, icon } = paymentMethodData[validPaymentMethod]; + return ( + + + + {label} +
{icon}
+ + } + name="paymentMethod" + value={validPaymentMethod} + cssOverrides={ + selected ? checkedRadioLabelColour : defaultRadioLabelColour + } + onChange={() => { + setPaymentMethod(validPaymentMethod); + setPaymentMethodError(undefined); + // Track payment method selection with QM + sendEventPaymentMethodSelected(validPaymentMethod); + }} + /> +
+ {validPaymentMethod === 'Stripe' && selected && ( +
+ + { + // no-op + }} + onExpiryChange={() => { + // no-op + }} + onCvcChange={() => { + // no-op + }} + errors={{}} + recaptcha={ + { + setStripeClientSecretInProgress(true); + setRecaptchaToken(token); + void stripeCreateSetupIntentRecaptcha( + isTestUser, + stripePublicKey, + token, + ).then((client_secret) => { + setStripeClientSecret(client_secret); + setStripeClientSecretInProgress(false); + }); + }} + onRecaptchaExpired={() => { + setRecaptchaToken(undefined); + }} + /> + } + /> +
+ )} + + {validPaymentMethod === 'DirectDebit' && selected && ( +
+ { + setAccountHolderName(name); + }} + updateAccountNumber={(number: string) => { + setAccountNumber(number); + }} + updateSortCode={(sortCode: string) => { + setSortCode(sortCode); + }} + updateAccountHolderConfirmation={( + confirmation: boolean, + ) => { + setAccountHolderConfirmation(confirmation); + }} + recaptcha={ + { + setRecaptchaToken(token); + }} + onRecaptchaExpired={() => { + setRecaptchaToken(undefined); + }} + /> + } + formError={''} + errors={{}} + /> +
+ )} +
+ ); + })} +
+
+ ); +} From 410324b1951778a1e9ea37c906607d30b56d708b Mon Sep 17 00:00:00 2001 From: andrewHEguardian <114918544+andrewHEguardian@users.noreply.github.com> Date: Thu, 2 Jan 2025 15:36:31 +0000 Subject: [PATCH 2/4] refactor: move dd and validpayment methods inside component --- .../components/checkoutComponent.tsx | 38 +----------- .../components/paymentSection.tsx | 60 ++++++++++++------- 2 files changed, 41 insertions(+), 57 deletions(-) diff --git a/support-frontend/assets/pages/[countryGroupId]/components/checkoutComponent.tsx b/support-frontend/assets/pages/[countryGroupId]/components/checkoutComponent.tsx index fe3587cac4..c86d966edf 100644 --- a/support-frontend/assets/pages/[countryGroupId]/components/checkoutComponent.tsx +++ b/support-frontend/assets/pages/[countryGroupId]/components/checkoutComponent.tsx @@ -47,13 +47,10 @@ import type { } from 'helpers/forms/paymentIntegrations/readerRevenueApis'; import { DirectDebit, - isPaymentMethod, type PaymentMethod as LegacyPaymentMethod, PayPal, Stripe, - toPaymentMethodSwitchNaming, } from 'helpers/forms/paymentMethods'; -import { isSwitchOn } from 'helpers/globalsAndSwitches/globals'; import type { AppConfig } from 'helpers/globalsAndSwitches/window'; import type { IsoCountry } from 'helpers/internationalisation/country'; import { countryGroups } from 'helpers/internationalisation/countryGroup'; @@ -123,12 +120,6 @@ const PaymentSection = lazy(() => import('./paymentSection')); type PaymentMethod = LegacyPaymentMethod | 'StripeExpressCheckoutElement'; const countriesRequiringBillingState = ['US', 'CA', 'AU']; -function paymentMethodIsActive(paymentMethod: LegacyPaymentMethod) { - return isSwitchOn( - `recurringPaymentMethods.${toPaymentMethodSwitchNaming(paymentMethod)}`, - ); -} - /** /** * Attempt to submit a payment to the server. The response will be either `success`, `failure` or `pending`. @@ -269,18 +260,6 @@ export function CheckoutComponent({ return
Invalid Amount {originalAmount}
; } - const validPaymentMethods = [ - /* NOT YET IMPLEMENTED - countryGroupId === 'EURCountries' && Sepa, - countryId === 'US' && AmazonPay, - */ - countryId === 'GB' && DirectDebit, - Stripe, - PayPal, - ] - .filter(isPaymentMethod) - .filter(paymentMethodIsActive); - const [paymentMethod, setPaymentMethod] = useState(); const [paymentMethodError, setPaymentMethodError] = useState(); @@ -381,13 +360,6 @@ export function CheckoutComponent({ const formRef = useRef(null); - /** Direct debit details */ - const [accountHolderName, setAccountHolderName] = useState(''); - const [accountNumber, setAccountNumber] = useState(''); - const [sortCode, setSortCode] = useState(''); - const [accountHolderConfirmation, setAccountHolderConfirmation] = - useState(false); - const [isProcessingPayment, setIsProcessingPayment] = useState(false); /** General error that can occur via fetch validations */ @@ -1124,7 +1096,6 @@ export function CheckoutComponent({ Loading...}> void; setStripeClientSecret: (clientSecret: string) => void; setStripeClientSecretInProgress: (inProgress: boolean) => void; recaptchaToken: string | undefined; setRecaptchaToken: (token: string | undefined) => void; - accountHolderName: string; - setAccountHolderName: (name: string) => void; - accountNumber: string; - setAccountNumber: (number: string) => void; - sortCode: string; - setSortCode: (sortCode: string) => void; - accountHolderConfirmation: boolean; - setAccountHolderConfirmation: (confirmation: boolean) => void; paymentMethod: PaymentMethod | 'StripeExpressCheckoutElement' | undefined; setPaymentMethod: (paymentMethod: PaymentMethod) => void; sectionNumber: number; stripePublicKey: string; isTestUser: boolean; countryGroupId: CountryGroupId; + countryId: IsoCountry; }; export default function PaymentSection({ - validPaymentMethods, paymentMethodError, setPaymentMethodError, setStripeClientSecret, setStripeClientSecretInProgress, recaptchaToken, setRecaptchaToken, - accountHolderName, - setAccountHolderName, - accountNumber, - setAccountNumber, - sortCode, - setSortCode, - accountHolderConfirmation, - setAccountHolderConfirmation, + paymentMethod, setPaymentMethod, sectionNumber, stripePublicKey, isTestUser, countryGroupId, + countryId, }: PaymentSectionProps) { + const validPaymentMethods = [ + /* NOT YET IMPLEMENTED + countryGroupId === 'EURCountries' && Sepa, + countryId === 'US' && AmazonPay, + */ + countryId === 'GB' && DirectDebit, + Stripe, + PayPal, + ] + .filter(isPaymentMethod) + .filter(paymentMethodIsActive); + + /** Direct debit details */ + const [accountHolderName, setAccountHolderName] = useState(''); + const [accountNumber, setAccountNumber] = useState(''); + const [sortCode, setSortCode] = useState(''); + const [accountHolderConfirmation, setAccountHolderConfirmation] = + useState(false); + return ( From cb79b321a9fadccc7fcc831fc10bdf816846e0fe Mon Sep 17 00:00:00 2001 From: andrewHEguardian <114918544+andrewHEguardian@users.noreply.github.com> Date: Thu, 2 Jan 2025 16:15:12 +0000 Subject: [PATCH 3/4] lazy load express checkout --- .../components/checkoutComponent.tsx | 161 +++------------- .../components/expressCheckout.tsx | 180 ++++++++++++++++++ .../components/paymentSection.tsx | 1 - 3 files changed, 204 insertions(+), 138 deletions(-) create mode 100644 support-frontend/assets/pages/[countryGroupId]/components/expressCheckout.tsx diff --git a/support-frontend/assets/pages/[countryGroupId]/components/checkoutComponent.tsx b/support-frontend/assets/pages/[countryGroupId]/components/checkoutComponent.tsx index c86d966edf..b7048f7e08 100644 --- a/support-frontend/assets/pages/[countryGroupId]/components/checkoutComponent.tsx +++ b/support-frontend/assets/pages/[countryGroupId]/components/checkoutComponent.tsx @@ -2,13 +2,11 @@ import { css } from '@emotion/react'; import { palette, space } from '@guardian/source/foundations'; import { Checkbox, Label, TextInput } from '@guardian/source/react-components'; import { - Divider, ErrorSummary, InfoSummary, } from '@guardian/source-development-kitchen/react-components'; import { CardNumberElement, - ExpressCheckoutElement, useElements, useStripe, } from '@stripe/react-stripe-js'; @@ -71,9 +69,7 @@ import { getSupportAbTests, } from 'helpers/tracking/acquisitions'; import { trackComponentClick } from 'helpers/tracking/behaviour'; -import { sendEventPaymentMethodSelected } from 'helpers/tracking/quantumMetric'; import { isProd } from 'helpers/urls/url'; -import { logException } from 'helpers/utilities/logger'; import type { GeoId } from 'pages/geoIdConfig'; import { getGeoIdConfig } from 'pages/geoIdConfig'; import { CheckoutDivider } from 'pages/supporter-plus-landing/components/checkoutDivider'; @@ -83,6 +79,7 @@ import { PaymentTsAndCs, SummaryTsAndCs, } from 'pages/supporter-plus-landing/components/paymentTsAndCs'; +import { LoadingDots } from '../../../../stories/animations/LoadingDots.stories'; import { formatMachineDate, formatUserDate, @@ -111,7 +108,7 @@ import { retryPaymentStatus } from './retryPaymentStatus'; import { setThankYouOrder, unsetThankYouOrder } from './thankYouComponent'; const PaymentSection = lazy(() => import('./paymentSection')); - +const ExpressCheckout = lazy(() => import('./expressCheckout')); /** * We have not added StripeExpressCheckoutElement to the old PaymentMethod * as it is heavily coupled through the code base and would require adding @@ -274,8 +271,6 @@ export function CheckoutComponent({ ] = useState(); const [stripeExpressCheckoutSuccessful, setStripeExpressCheckoutSuccessful] = useState(false); - const [stripeExpressCheckoutReady, setStripeExpressCheckoutReady] = - useState(false); useEffect(() => { if (stripeExpressCheckoutSuccessful) { formRef.current?.requestSubmit(); @@ -366,9 +361,6 @@ export function CheckoutComponent({ const [errorMessage, setErrorMessage] = useState(); const [errorContext, setErrorContext] = useState(); - const useLinkExpressCheckout = - abParticipations.linkExpressCheckout === 'variant'; - const formOnSubmit = async (formData: FormData) => { setIsProcessingPayment(true); /** @@ -730,133 +722,28 @@ export function CheckoutComponent({ {useStripeExpressCheckout && ( -
- { - /** - * This is use to show UI needed besides this Element - * i.e. The "or" divider - */ - if (availablePaymentMethods) { - setStripeExpressCheckoutReady(true); - } - }} - onClick={({ resolve }) => { - /** @see https://docs.stripe.com/elements/express-checkout-element/accept-a-payment?locale=en-GB#handle-click-event */ - const options = { - emailRequired: true, - }; - - // Track payment method selection with QM - sendEventPaymentMethodSelected( - 'StripeExpressCheckoutElement', - ); - - resolve(options); - }} - onConfirm={async (event) => { - if (!(stripe && elements)) { - console.error('Stripe not loaded'); - return; - } - - const { error: submitError } = await elements.submit(); - - if (submitError) { - setErrorMessage(submitError.message); - return; - } - - const name = event.billingDetails?.name ?? ''; - - /** - * splits by the last space, and uses the head as firstName - * and tail as lastName - */ - const firstName = name - .substring(0, name.lastIndexOf(' ') + 1) - .trim(); - const lastName = name - .substring(name.lastIndexOf(' ') + 1, name.length) - .trim(); - setFirstName(firstName); - setLastName(lastName); - - event.billingDetails?.address.postal_code && - setBillingPostcode( - event.billingDetails.address.postal_code, - ); - - if ( - !event.billingDetails?.address.state && - countriesRequiringBillingState.includes(countryId) - ) { - logException( - "Could not find state from Stripe's billingDetails", - { geoId, countryGroupId, countryId }, - ); - } - event.billingDetails?.address.state && - setBillingState(event.billingDetails.address.state); - - event.billingDetails?.email && - setEmail(event.billingDetails.email); - - setPaymentMethod('StripeExpressCheckoutElement'); - setStripeExpressCheckoutPaymentType( - event.expressPaymentType, - ); - /** - * There is a useEffect that listens to this and submits the form - * when true - */ - setStripeExpressCheckoutSuccessful(true); - }} - options={{ - paymentMethods: { - applePay: 'auto', - googlePay: 'auto', - link: useLinkExpressCheckout ? 'auto' : 'never', - }, - }} + }> + - - {stripeExpressCheckoutReady && ( - - )} -
+ )} 1. Your details @@ -1094,7 +981,7 @@ export function CheckoutComponent({ )} - Loading...}> + }> void; + stripe: Stripe | null; + elements: StripeElements | null; + setErrorMessage: (message: string | undefined) => void; + setFirstName: (firstName: string) => void; + setLastName: (lastName: string) => void; + setBillingPostcode: (postcode: string) => void; + setBillingState: (state: string) => void; + setEmail: (email: string) => void; + setPaymentMethod: ( + paymentMethod: PaymentMethod | 'StripeExpressCheckoutElement', + ) => void; + setStripeExpressCheckoutSuccessful: (successful: boolean) => void; + countryId: IsoCountry; + geoId: GeoId; + countryGroupId: CountryGroupId; +}; + +export default function ExpressCheckout({ + setStripeExpressCheckoutPaymentType, + stripe, + elements, + setErrorMessage, + setFirstName, + setLastName, + setBillingPostcode, + setBillingState, + setEmail, + setPaymentMethod, + setStripeExpressCheckoutSuccessful, + countryId, + geoId, + countryGroupId, +}: ExpressCheckoutProps) { + const [stripeExpressCheckoutReady, setStripeExpressCheckoutReady] = + useState(false); + + return ( +
+ { + /** + * This is use to show UI needed besides this Element + * i.e. The "or" divider + */ + if (availablePaymentMethods) { + setStripeExpressCheckoutReady(true); + } + }} + onClick={({ resolve }) => { + /** @see https://docs.stripe.com/elements/express-checkout-element/accept-a-payment?locale=en-GB#handle-click-event */ + const options = { + emailRequired: true, + }; + + // Track payment method selection with QM + sendEventPaymentMethodSelected('StripeExpressCheckoutElement'); + + resolve(options); + }} + onConfirm={async (event) => { + if (!(stripe && elements)) { + console.error('Stripe not loaded'); + return; + } + + const { error: submitError } = await elements.submit(); + + if (submitError) { + setErrorMessage(submitError.message); + return; + } + + const name = event.billingDetails?.name ?? ''; + + /** + * splits by the last space, and uses the head as firstName + * and tail as lastName + */ + const firstName = name.substring(0, name.lastIndexOf(' ') + 1).trim(); + const lastName = name + .substring(name.lastIndexOf(' ') + 1, name.length) + .trim(); + setFirstName(firstName); + setLastName(lastName); + + event.billingDetails?.address.postal_code && + setBillingPostcode(event.billingDetails.address.postal_code); + + if ( + !event.billingDetails?.address.state && + countriesRequiringBillingState.includes(countryId) + ) { + logException("Could not find state from Stripe's billingDetails", { + geoId, + countryGroupId, + countryId, + }); + } + event.billingDetails?.address.state && + setBillingState(event.billingDetails.address.state); + + event.billingDetails?.email && setEmail(event.billingDetails.email); + + setPaymentMethod('StripeExpressCheckoutElement'); + setStripeExpressCheckoutPaymentType(event.expressPaymentType); + /** + * There is a useEffect that listens to this and submits the form + * when true + */ + setStripeExpressCheckoutSuccessful(true); + }} + options={{ + paymentMethods: { + applePay: 'auto', + googlePay: 'auto', + link: 'auto', + }, + }} + /> + + {stripeExpressCheckoutReady && ( + + )} +
+ ); +} diff --git a/support-frontend/assets/pages/[countryGroupId]/components/paymentSection.tsx b/support-frontend/assets/pages/[countryGroupId]/components/paymentSection.tsx index 8b631e65f4..d370f11ae3 100644 --- a/support-frontend/assets/pages/[countryGroupId]/components/paymentSection.tsx +++ b/support-frontend/assets/pages/[countryGroupId]/components/paymentSection.tsx @@ -58,7 +58,6 @@ export default function PaymentSection({ setStripeClientSecretInProgress, recaptchaToken, setRecaptchaToken, - paymentMethod, setPaymentMethod, sectionNumber, From 79af00477dd660642bb2aefdb231a03fe6cf0d6b Mon Sep 17 00:00:00 2001 From: andrewHEguardian <114918544+andrewHEguardian@users.noreply.github.com> Date: Thu, 2 Jan 2025 16:30:47 +0000 Subject: [PATCH 4/4] turn off link --- .../pages/[countryGroupId]/components/expressCheckout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support-frontend/assets/pages/[countryGroupId]/components/expressCheckout.tsx b/support-frontend/assets/pages/[countryGroupId]/components/expressCheckout.tsx index ff7601cc87..43e1726c4b 100644 --- a/support-frontend/assets/pages/[countryGroupId]/components/expressCheckout.tsx +++ b/support-frontend/assets/pages/[countryGroupId]/components/expressCheckout.tsx @@ -140,7 +140,7 @@ export default function ExpressCheckout({ paymentMethods: { applePay: 'auto', googlePay: 'auto', - link: 'auto', + link: 'never', }, }} />