From 235e285fd783e584ff6e160367e6c9d51124ec35 Mon Sep 17 00:00:00 2001 From: Hugo Montenegro Date: Tue, 23 Jun 2026 09:24:12 -0700 Subject: [PATCH 1/2] chore(add-money): PIX-BR onramp recovery toggle + maintenance-flag cleanup Draft follow-up to #2268, to merge once the BRL-via-PIX onramp (Manteca Brazil) is stable again: flips pixBrazilOnrampMaintenance off so the "Maintenance" tag + in-flow banner disappear. The warn-only machinery stays in place, ready to flip back on if the onramp degrades again. Also fixes two smells surfaced in the #2268 review: - both surfaces (the list tag and the in-flow banner) now read the maintenance state through one isPixBrazilOnrampUnderMaintenance() selector instead of two separate flag reads that could drift apart if the rule ever gains complexity (e.g. a time window or extra regions). - the maintenance test snapshots and restores the shipped flag instead of hardcoding the restore value to `true`, so it no longer corrupts shared module state now that the committed default is `false`. --- .../AddMoney/components/MantecaAddMoney.tsx | 8 ++++---- .../AddWithdraw/AddWithdrawCountriesList.tsx | 13 ++++++------- .../__tests__/AddWithdrawCountriesList.test.tsx | 7 ++++++- src/config/underMaintenance.config.ts | 17 +++++++++++++++-- 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/components/AddMoney/components/MantecaAddMoney.tsx b/src/components/AddMoney/components/MantecaAddMoney.tsx index a43a54724..583b18d8a 100644 --- a/src/components/AddMoney/components/MantecaAddMoney.tsx +++ b/src/components/AddMoney/components/MantecaAddMoney.tsx @@ -26,7 +26,7 @@ import { useLimitsValidation } from '@/features/limits/hooks/useLimitsValidation import posthog from 'posthog-js' import { ANALYTICS_EVENTS } from '@/constants/analytics.consts' import InfoCard from '@/components/Global/InfoCard' -import underMaintenanceConfig, { PIX_BRAZIL_ONRAMP_MAINTENANCE } from '@/config/underMaintenance.config' +import { PIX_BRAZIL_ONRAMP_MAINTENANCE, isPixBrazilOnrampUnderMaintenance } from '@/config/underMaintenance.config' // Step type for URL state type MantecaStep = 'inputAmount' | 'depositDetails' @@ -72,9 +72,9 @@ const MantecaAddMoney: FC = () => { return countryData.find((country) => country.type === 'country' && country.path === selectedCountryPath) }, [selectedCountryPath]) const onBack = useSafeBack(addMoneyCountryUrl(selectedCountryPath)) - // BRL-via-PIX onramp warn-only maintenance flag (see underMaintenance.config.ts). + // BRL-via-PIX onramp warn-only maintenance banner (see underMaintenance.config.ts). // Brazil-scoped so the Argentina/ARS Manteca onramp is unaffected. - const showPixMaintenance = selectedCountry?.id === 'BR' && underMaintenanceConfig.pixBrazilOnrampMaintenance + const showPixMaintenanceBanner = selectedCountry?.id === 'BR' && isPixBrazilOnrampUnderMaintenance() // The pool→full upgrade gate asks "did the user clear ID verification?", // not "do they have an enabled rail elsewhere?" — read the identity // signal directly (Sumsub-cleared the human) instead of the old @@ -286,7 +286,7 @@ const MantecaAddMoney: FC = () => { limitsCurrency={limitsValidation.currency} onBack={onBack} maintenanceBanner={ - showPixMaintenance ? ( + showPixMaintenanceBanner ? ( {
{paymentMethods.map((method, index) => { // BRL-via-PIX onramp is warn-only under maintenance: tag the Pix option but - // keep it clickable (do not set isDisabled). - const isPixOnrampUnderMaintenance = - flow === 'add' && - method.id === 'pix-add' && - underMaintenanceConfig.pixBrazilOnrampMaintenance + // keep it clickable (do not set isDisabled). The `pix-add` method is itself + // Brazil-only (consts filter it to countryCode === 'BR'). + const showPixMaintenanceTag = + flow === 'add' && method.id === 'pix-add' && isPixBrazilOnrampUnderMaintenance() return ( { rightContent={ method.isSoon ? ( - ) : isPixOnrampUnderMaintenance ? ( + ) : showPixMaintenanceTag ? ( { * (config: pixBrazilOnrampMaintenance) — warn-only: it stays visible and clickable. */ describe('AddWithdrawCountriesList — PIX onramp maintenance tag', () => { + // snapshot/restore the shipped flag so each test can flip it without leaking state — + // and without coupling the restore to whatever the committed default happens to be + let originalPixMaintenance: boolean + beforeEach(() => { mockPush.mockClear() // a ready gate so a click can navigate — proving the option is not blocked setCapabilities('ready', [{ status: 'enabled', channel: 'bank', country: 'US' }]) + originalPixMaintenance = underMaintenanceConfig.pixBrazilOnrampMaintenance }) afterEach(() => { - underMaintenanceConfig.pixBrazilOnrampMaintenance = true + underMaintenanceConfig.pixBrazilOnrampMaintenance = originalPixMaintenance }) it('tags the Pix option "Maintenance" but keeps it clickable (warn-only)', () => { diff --git a/src/config/underMaintenance.config.ts b/src/config/underMaintenance.config.ts index abbf63fb8..5c07faada 100644 --- a/src/config/underMaintenance.config.ts +++ b/src/config/underMaintenance.config.ts @@ -38,7 +38,8 @@ * - shows a "Maintenance" tag on the Pix option in /add-money/brazil * - shows a warning banner inside the deposit flow (/add-money/brazil/manteca) * - does NOT block deposits — the option stays usable (warn-only) - * - set to false when PIX deposits are stable again + * - set to true when PIX deposits degrade again; both surfaces read it through + * isPixBrazilOnrampUnderMaintenance() so they can never drift apart * * note: if either mode is enabled, the maintenance banner will show everywhere * @@ -65,7 +66,7 @@ const underMaintenanceConfig: MaintenanceConfig = { disableXchainWithdraw: true, // set to true to disable cross-chain withdrawals (only allows USDC on Arbitrum) disableXchainSend: true, // set to true to disable cross-chain sends (claim, request payments - only allows USDC on Arbitrum) disableCardPioneers: true, // set to false to enable the Card Pioneers waitlist feature - pixBrazilOnrampMaintenance: true, // set to false when BRL-via-PIX deposits are stable again + pixBrazilOnrampMaintenance: false, // set to true when BRL-via-PIX deposits degrade again } // shared user-facing copy for cross-chain disabled paths — keep wording aligned with TokenSelector banner @@ -81,4 +82,16 @@ export const PIX_BRAZIL_ONRAMP_MAINTENANCE = { 'PIX deposits are currently unstable and may be delayed or fail. You can still continue, but service may be unreliable until this is resolved.', } +/** + * Whether the BRL-via-PIX onramp (Manteca Brazil deposit) is flagged warn-only under maintenance. + * + * Single source of truth for the two surfaces that warn about it — the "Maintenance" tag on the + * Pix option in the add-money country list, and the in-flow banner on /add-money/brazil/manteca. + * Callers add only their own context check (the `pix-add` method, or country `BR`); the maintenance + * determination lives here so the two surfaces can never drift apart. + */ +export function isPixBrazilOnrampUnderMaintenance(): boolean { + return underMaintenanceConfig.pixBrazilOnrampMaintenance +} + export default underMaintenanceConfig From 79f13417724fcb3b6b79e7c177d103ef7724a6d2 Mon Sep 17 00:00:00 2001 From: Hugo Montenegro Date: Tue, 23 Jun 2026 09:58:13 -0700 Subject: [PATCH 2/2] chore(maintenance): coherent naming + single import alias across maintenance surfaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Self-review follow-up on the #2268 surfaces — the maintenance config had drifted into mixed naming styles and read patterns. Make them consistent: - enablePixOnrampMaintenanceWarning replaces the noun-shaped pixBrazilOnrampMaintenance so it reads like its true siblings (enableFullMaintenance, enableMaintenanceBanner): verb-first, true = the maintenance behavior is ON. The config header now documents the convention (enable vs disable) so new flags can't reintroduce the ambiguity. - drop the lone isPixBrazilOnrampUnderMaintenance() getter and read the flag inline like every other flag in the file. The shared copy constant already de-dupes the only thing worth sharing; each surface's context check (pix-add method / country BR) is legitimately local. - PIX_ONRAMP_MAINTENANCE_COPY replaces PIX_BRAZIL_ONRAMP_MAINTENANCE — drops the redundant "Brazil" (PIX is Brazil-only) and marks it as copy. - read the config under one name everywhere (underMaintenanceConfig); three files had aliased the default import to maintenanceConfig. The enable*/disable* polarity of the existing flags is deliberately left untouched: flipping booleans across a prod kill-switch file is an outage risk and out of scope here. The new header convention documents that polarity rather than changing it. --- .../qr-pay/__tests__/qr-pay-states.test.tsx | 6 ++-- src/app/(mobile-ui)/qr-pay/page.tsx | 4 +-- .../AddMoney/components/MantecaAddMoney.tsx | 9 +++--- .../AddWithdraw/AddWithdrawCountriesList.tsx | 8 +++-- .../AddWithdrawCountriesList.test.tsx | 10 +++--- src/components/Global/Banner/index.tsx | 4 +-- src/config/underMaintenance.config.ts | 31 +++++++------------ src/proxy.ts | 4 +-- 8 files changed, 36 insertions(+), 40 deletions(-) diff --git a/src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx b/src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx index cdcfc5213..6869b56be 100644 --- a/src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx +++ b/src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx @@ -866,8 +866,8 @@ describe('GROUP 2: Payment Form States', () => { }) test('Provider maintenance shows maintenance banner', async () => { - const maintenanceConfig = require('@/config/underMaintenance.config').default - maintenanceConfig.disabledPaymentProviders = ['MANTECA'] + const underMaintenanceConfig = require('@/config/underMaintenance.config').default + underMaintenanceConfig.disabledPaymentProviders = ['MANTECA'] setupMantecaPayment() @@ -878,7 +878,7 @@ describe('GROUP 2: Payment Form States', () => { }) // Clean up - maintenanceConfig.disabledPaymentProviders = [] + underMaintenanceConfig.disabledPaymentProviders = [] }) }) diff --git a/src/app/(mobile-ui)/qr-pay/page.tsx b/src/app/(mobile-ui)/qr-pay/page.tsx index c2012710f..df67237ad 100644 --- a/src/app/(mobile-ui)/qr-pay/page.tsx +++ b/src/app/(mobile-ui)/qr-pay/page.tsx @@ -61,7 +61,7 @@ import { PointsAction } from '@/services/services.types' import { usePointsConfetti } from '@/hooks/usePointsConfetti' import { usePointsCalculation } from '@/hooks/usePointsCalculation' import { useModalsContext } from '@/context/ModalsContext' -import maintenanceConfig from '@/config/underMaintenance.config' +import underMaintenanceConfig from '@/config/underMaintenance.config' import PointsCard from '@/components/Common/PointsCard' import { TRANSACTIONS } from '@/constants/query.consts' import { useLimitsValidation } from '@/features/limits/hooks/useLimitsValidation' @@ -141,7 +141,7 @@ export default function QRPayPage() { // Check if this payment provider is under maintenance const isProviderDisabled = useMemo(() => { - return paymentProcessor ? maintenanceConfig.disabledPaymentProviders.includes(paymentProcessor) : false + return paymentProcessor ? underMaintenanceConfig.disabledPaymentProviders.includes(paymentProcessor) : false }, [paymentProcessor]) // MIGRATION-REVIEW: QR-pay KYC gate, formerly useQrKycGate + useKycStatus. diff --git a/src/components/AddMoney/components/MantecaAddMoney.tsx b/src/components/AddMoney/components/MantecaAddMoney.tsx index 583b18d8a..1aa6f7fec 100644 --- a/src/components/AddMoney/components/MantecaAddMoney.tsx +++ b/src/components/AddMoney/components/MantecaAddMoney.tsx @@ -26,7 +26,7 @@ import { useLimitsValidation } from '@/features/limits/hooks/useLimitsValidation import posthog from 'posthog-js' import { ANALYTICS_EVENTS } from '@/constants/analytics.consts' import InfoCard from '@/components/Global/InfoCard' -import { PIX_BRAZIL_ONRAMP_MAINTENANCE, isPixBrazilOnrampUnderMaintenance } from '@/config/underMaintenance.config' +import underMaintenanceConfig, { PIX_ONRAMP_MAINTENANCE_COPY } from '@/config/underMaintenance.config' // Step type for URL state type MantecaStep = 'inputAmount' | 'depositDetails' @@ -74,7 +74,8 @@ const MantecaAddMoney: FC = () => { const onBack = useSafeBack(addMoneyCountryUrl(selectedCountryPath)) // BRL-via-PIX onramp warn-only maintenance banner (see underMaintenance.config.ts). // Brazil-scoped so the Argentina/ARS Manteca onramp is unaffected. - const showPixMaintenanceBanner = selectedCountry?.id === 'BR' && isPixBrazilOnrampUnderMaintenance() + const showPixMaintenanceBanner = + selectedCountry?.id === 'BR' && underMaintenanceConfig.enablePixOnrampMaintenanceWarning // The pool→full upgrade gate asks "did the user clear ID verification?", // not "do they have an enabled rail elsewhere?" — read the identity // signal directly (Sumsub-cleared the human) instead of the old @@ -290,8 +291,8 @@ const MantecaAddMoney: FC = () => { ) : undefined } diff --git a/src/components/AddWithdraw/AddWithdrawCountriesList.tsx b/src/components/AddWithdraw/AddWithdrawCountriesList.tsx index f4cd26aee..c857950c8 100644 --- a/src/components/AddWithdraw/AddWithdrawCountriesList.tsx +++ b/src/components/AddWithdraw/AddWithdrawCountriesList.tsx @@ -35,7 +35,7 @@ import { getRegionIntent } from '@/utils/regions.utils' import { useTosGuard } from '@/hooks/useTosGuard' import { BridgeTosStep } from '@/components/Kyc/BridgeTosStep' import { useModalsContext } from '@/context/ModalsContext' -import { PIX_BRAZIL_ONRAMP_MAINTENANCE, isPixBrazilOnrampUnderMaintenance } from '@/config/underMaintenance.config' +import underMaintenanceConfig, { PIX_ONRAMP_MAINTENANCE_COPY } from '@/config/underMaintenance.config' interface AddWithdrawCountriesListProps { flow: 'add' | 'withdraw' @@ -432,7 +432,9 @@ const AddWithdrawCountriesList = ({ flow }: AddWithdrawCountriesListProps) => { // keep it clickable (do not set isDisabled). The `pix-add` method is itself // Brazil-only (consts filter it to countryCode === 'BR'). const showPixMaintenanceTag = - flow === 'add' && method.id === 'pix-add' && isPixBrazilOnrampUnderMaintenance() + flow === 'add' && + method.id === 'pix-add' && + underMaintenanceConfig.enablePixOnrampMaintenanceWarning return ( { ) : showPixMaintenanceTag ? ( ) : null diff --git a/src/components/AddWithdraw/__tests__/AddWithdrawCountriesList.test.tsx b/src/components/AddWithdraw/__tests__/AddWithdrawCountriesList.test.tsx index 4092549fc..7fbf92b8e 100644 --- a/src/components/AddWithdraw/__tests__/AddWithdrawCountriesList.test.tsx +++ b/src/components/AddWithdraw/__tests__/AddWithdrawCountriesList.test.tsx @@ -239,7 +239,7 @@ describe('AddWithdrawCountriesList — bank gate', () => { /** * BRL-via-PIX onramp is unstable, so the Pix option is flagged "under maintenance" - * (config: pixBrazilOnrampMaintenance) — warn-only: it stays visible and clickable. + * (config: enablePixOnrampMaintenanceWarning) — warn-only: it stays visible and clickable. */ describe('AddWithdrawCountriesList — PIX onramp maintenance tag', () => { // snapshot/restore the shipped flag so each test can flip it without leaking state — @@ -250,15 +250,15 @@ describe('AddWithdrawCountriesList — PIX onramp maintenance tag', () => { mockPush.mockClear() // a ready gate so a click can navigate — proving the option is not blocked setCapabilities('ready', [{ status: 'enabled', channel: 'bank', country: 'US' }]) - originalPixMaintenance = underMaintenanceConfig.pixBrazilOnrampMaintenance + originalPixMaintenance = underMaintenanceConfig.enablePixOnrampMaintenanceWarning }) afterEach(() => { - underMaintenanceConfig.pixBrazilOnrampMaintenance = originalPixMaintenance + underMaintenanceConfig.enablePixOnrampMaintenanceWarning = originalPixMaintenance }) it('tags the Pix option "Maintenance" but keeps it clickable (warn-only)', () => { - underMaintenanceConfig.pixBrazilOnrampMaintenance = true + underMaintenanceConfig.enablePixOnrampMaintenanceWarning = true render() @@ -271,7 +271,7 @@ describe('AddWithdrawCountriesList — PIX onramp maintenance tag', () => { }) it('shows no maintenance tag when the flag is off, and never tags non-Pix methods', () => { - underMaintenanceConfig.pixBrazilOnrampMaintenance = false + underMaintenanceConfig.enablePixOnrampMaintenanceWarning = false render() diff --git a/src/components/Global/Banner/index.tsx b/src/components/Global/Banner/index.tsx index fbddaecc8..c987d97aa 100644 --- a/src/components/Global/Banner/index.tsx +++ b/src/components/Global/Banner/index.tsx @@ -4,7 +4,7 @@ import { useEffect } from 'react' import { usePathname } from 'next/navigation' import { MaintenanceBanner } from './MaintenanceBanner' import { MarqueeWrapper } from '../MarqueeWrapper' -import maintenanceConfig from '@/config/underMaintenance.config' +import underMaintenanceConfig from '@/config/underMaintenance.config' import { HandThumbsUp } from '@/assets' import Image from 'next/image' import { useModalsContext } from '@/context/ModalsContext' @@ -16,7 +16,7 @@ export function Banner() { if (!pathname) return null // check if maintenance banner OR full maintenance is enabled - show on all pages - if (maintenanceConfig.enableMaintenanceBanner || maintenanceConfig.enableFullMaintenance) { + if (underMaintenanceConfig.enableMaintenanceBanner || underMaintenanceConfig.enableFullMaintenance) { return } diff --git a/src/config/underMaintenance.config.ts b/src/config/underMaintenance.config.ts index 5c07faada..373e91157 100644 --- a/src/config/underMaintenance.config.ts +++ b/src/config/underMaintenance.config.ts @@ -3,6 +3,12 @@ * * to enable maintenance mode, simply toggle one or both of these keys: * + * naming convention — read the verb, not just the value: + * - enable: true = turn that maintenance behavior ON (block / redirect / warn) + * - disable: true = turn that product feature OFF + * `enableX: true` makes X happen; `disableY: true` makes Y stop. New flags MUST follow + * one of these two shapes so the polarity is always obvious from the name. + * * 1. enableFullMaintenance: redirects ALL pages to /maintenance page * - landing page (/) and support page (/support) remain accessible * - maintenance banner shows on all pages (including landing and support) @@ -34,12 +40,11 @@ * - card pioneer modal, carousel cta, and perk rewards hidden from home * - set to false to enable the feature * - * 7. pixBrazilOnrampMaintenance: warn-only flag for the BRL-via-PIX onramp (Manteca Brazil deposit) + * 7. enablePixOnrampMaintenanceWarning: warn-only flag for the PIX onramp (Manteca Brazil deposit) * - shows a "Maintenance" tag on the Pix option in /add-money/brazil * - shows a warning banner inside the deposit flow (/add-money/brazil/manteca) * - does NOT block deposits — the option stays usable (warn-only) - * - set to true when PIX deposits degrade again; both surfaces read it through - * isPixBrazilOnrampUnderMaintenance() so they can never drift apart + * - set to true when PIX deposits degrade again * * note: if either mode is enabled, the maintenance banner will show everywhere * @@ -56,7 +61,7 @@ interface MaintenanceConfig { disableXchainWithdraw: boolean disableXchainSend: boolean disableCardPioneers: boolean - pixBrazilOnrampMaintenance: boolean + enablePixOnrampMaintenanceWarning: boolean } const underMaintenanceConfig: MaintenanceConfig = { @@ -66,32 +71,20 @@ const underMaintenanceConfig: MaintenanceConfig = { disableXchainWithdraw: true, // set to true to disable cross-chain withdrawals (only allows USDC on Arbitrum) disableXchainSend: true, // set to true to disable cross-chain sends (claim, request payments - only allows USDC on Arbitrum) disableCardPioneers: true, // set to false to enable the Card Pioneers waitlist feature - pixBrazilOnrampMaintenance: false, // set to true when BRL-via-PIX deposits degrade again + enablePixOnrampMaintenanceWarning: false, // set to true when PIX (Manteca Brazil) deposits degrade again } // shared user-facing copy for cross-chain disabled paths — keep wording aligned with TokenSelector banner export const CROSS_CHAIN_DISABLED_MESSAGE = 'Cross-chain claims are temporarily unavailable. Try claiming to an external wallet on the same chain as the link, or try again later.' -// shared user-facing copy for the BRL-via-PIX onramp maintenance warning — keep the list tag and +// shared user-facing copy for the PIX onramp maintenance warning — keep the list tag and // the in-flow banner aligned -export const PIX_BRAZIL_ONRAMP_MAINTENANCE = { +export const PIX_ONRAMP_MAINTENANCE_COPY = { badge: 'Maintenance', title: 'PIX deposits are under maintenance', description: 'PIX deposits are currently unstable and may be delayed or fail. You can still continue, but service may be unreliable until this is resolved.', } -/** - * Whether the BRL-via-PIX onramp (Manteca Brazil deposit) is flagged warn-only under maintenance. - * - * Single source of truth for the two surfaces that warn about it — the "Maintenance" tag on the - * Pix option in the add-money country list, and the in-flow banner on /add-money/brazil/manteca. - * Callers add only their own context check (the `pix-add` method, or country `BR`); the maintenance - * determination lives here so the two surfaces can never drift apart. - */ -export function isPixBrazilOnrampUnderMaintenance(): boolean { - return underMaintenanceConfig.pixBrazilOnrampMaintenance -} - export default underMaintenanceConfig diff --git a/src/proxy.ts b/src/proxy.ts index 34b2cf5be..3c2e7eac4 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -3,7 +3,7 @@ // https://nextjs.org/docs/messages/middleware-to-proxy import type { NextRequest } from 'next/server' import { NextResponse } from 'next/server' -import maintenanceConfig from '@/config/underMaintenance.config' +import underMaintenanceConfig from '@/config/underMaintenance.config' export function proxy(request: NextRequest) { const { pathname } = request.nextUrl @@ -15,7 +15,7 @@ export function proxy(request: NextRequest) { // } // check if full maintenance mode is enabled - if (maintenanceConfig.enableFullMaintenance) { + if (underMaintenanceConfig.enableFullMaintenance) { const allowedPaths = ['/', '/maintenance', '/apple-app-site-association', '/support'] if ( !allowedPaths.includes(pathname) &&