'use client'; /** * The customer's vaulted payment methods. * * ⛔ AUTO-HIDES. `getMySavedPaymentMethods()` returns `[]` for everyone who has * never vaulted a card, which on a new store is everyone, so the whole section * renders null. It also renders null when the call fails or the shopper is * logged out. There is no empty state and no error box: a shopper who has no * saved cards does not need to be told about a feature they have not used. * * ⛔ THIS IS NOT ONE-CLICK CHECKOUT, and there is deliberately no "pay with * this card" button. The ONLY storefront call that charges a vaulted card is * `subscribeToMembership({ planId, savedPaymentTokenId })`. Nothing on the * normal checkout accepts a saved token, so a pay-with-saved-card button would * be a dead control. This is a read-only list. * * ⛔ NO CARD DATA IS HERE AND NONE IS AVAILABLE. The response carries display * fields only: `paymentMethod`, `brand`, `last4`, `expMonth`, `expYear`, * `isDefault`. Every one of those can be null except the first and last. * * Cards get here by checking out with `createPaymentIntent(checkoutId, * { saveCard: true })`, which is honored only when the checkout has a known * `customerId` (so never for a guest) and is silently ignored by providers * without tokenization (Grow today). Treat vaulting as best-effort and never * promise the shopper their card was definitely saved. */ import { useEffect, useState } from 'react'; import type { StorefrontSavedPaymentMethod } from 'brainerce'; import { getClient } from '@/core/lib/brainerce'; import { useTranslations } from '@/core/lib/translations'; import { useAuth } from '@/core/providers/store-provider'; import { cn } from '@/core/lib/utils'; /** "12/2027" from expMonth/expYear, or null when the provider gave neither. */ function formatExpiry(month: number | null, year: number | null): string | null { if (month == null || year == null) return null; return `${String(month).padStart(2, '0')}/${year}`; } export function SavedPaymentMethods({ className }: { className?: string }) { const t = useTranslations('account'); const { isLoggedIn } = useAuth(); const [methods, setMethods] = useState([]); useEffect(() => { if (!isLoggedIn) { setMethods([]); return; } let cancelled = false; getClient() .getMySavedPaymentMethods() .then((result) => { if (!cancelled) setMethods(Array.isArray(result) ? result : []); }) .catch(() => { // Swallowed on purpose. The account page must render its profile, // addresses and orders whether or not this optional list resolves. if (!cancelled) setMethods([]); }); return () => { cancelled = true; }; }, [isLoggedIn]); if (methods.length === 0) return null; return (

{t('paymentMethod')}

); }