Payment Method

Saved cards and accounts, with expiry warnings. A card expiring next month is the reason a subscription fails, and saying so before it does is the entire value of this list.

src/components/ui/payment-method.tsx

About

From the component's own source, where it was written.

Saved payment methods, selectable, with one default.

An expired or expiring card is flagged in the list rather than at checkout. The card that fails is almost always one that expired months ago and nobody told the customer — surfacing it here is the difference between a prompt and an involuntary churn.

Only the last four digits are ever rendered. A saved method has no business holding a full PAN client-side, and a component that accepts one invites somebody to pass it.

Install

npx astralyx-ui add payment-method

Usage

import { PaymentMethodList } from '@/components/ui/payment-method'

<PaymentMethodList methods={methods} selected={id} onSelect={setId} />

Composer

Change the props and the source below follows.

Props
tsx
<PaymentMethodList methods={methods} selected={id} onSelect={setId} />

Examples

Selectable list

API

Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.

PaymentMethodList

Saved payment methods, selectable, with one default.

Extends Omit<ComponentProps<'div'>, 'onSelect'>.

PropTypeDefaultDescription
methodsPaymentMethod[]required{ id, kind, last4, brand?, expiry?, holder?, isDefault? }. Only ever the last four digits — a full PAN has no business reaching a component.
selectedstring——
onSelect(id: string) => void—Omit any of them to render that affordance not at all, rather than disabled.
onRemove(id: string) => void—Omit any of them to render that affordance not at all, rather than disabled.
onSetDefault(id: string) => void—Omit any of them to render that affordance not at all, rather than disabled.
nowDate—Reference point for expiry, so tests are not time-dependent.
emptyLabelReactNode'No payment methods'—
defaultLabelReactNode'Default'Badge on the method charged by default.
expiredLabelReactNode'Expired'—
expiringLabelReactNode'Expiring soon'Badge on a card within 60 days of expiry.
expiresLabelReactNode'Expires'Precedes the MM/YY expiry.
setDefaultLabelReactNode'Make default'—
removeLabelReactNode'Remove'—
expiredNoteReactNode'An expired card will decline. Update it before the next charge.'Note shown when any card has already expired. Pass null to drop it.

Functions

FunctionSignatureDescription
expiryState(expiry: string | undefined, now: Date) => 'none' | 'expired' | 'expiring' | 'ok'Expired, or inside the window where the issuer reissues.

Types

PaymentMethod

FieldTypeDescription
idstring—
kind'card' | 'bank' | 'wallet'—
last4stringLast four digits or account suffix. Never the full number.
brand?ReactNode—
expiry?stringCards within 60 days of expiry are flagged, expired ones are marked and cannot be made default.
holder?ReactNode—
isDefault?boolean—