Tender Checkout
A card payment form with a live card that flips to show the CVC, brand detection, Luhn checks and a paced pay button.
Overview
The card on the left mirrors what you type. Digits drop into place, the brand mark appears as soon as the number identifies it, and the card turns over to show the signature strip when you reach the security code. The design is quietly premium: a graphite face, a fine guilloché line pattern and a gold chip, with no glossy gradients.
Formatting and validation run as you type. Numbers group by brand (4-6-5 for Amex), the Luhn checksum is verified, expiry dates reject past months, and focus moves forward once a field is complete. Errors appear on blur, never while someone is still typing.
The pay button shows three steps: a progress fill while processing, then a stroke-drawn check on success, or an inline error that keeps the form editable. This component is the interface only. In production, put your provider’s hosted card fields in the same layout (for PCI SAQ-A scope) and keep the states.
When to use
- Payment and purchase steps that need trust and clarity.
When not to use
- Taking real payments without a PCI-compliant processor behind it.
Installation
pnpm dlx shadcn@latest add https://sundaeui.dev/r/tender-checkout.jsonnpx shadcn@latest add https://sundaeui.dev/r/tender-checkout.jsonyarn dlx shadcn@latest add https://sundaeui.dev/r/tender-checkout.jsonbunx --bun shadcn@latest add https://sundaeui.dev/r/tender-checkout.jsonWorks in any React project. Files are written to components/sundae/. Read the source in the stage’s Code view. Pro assets need a personal token. See authenticated installs.
- No dependencies beyond React 18 or later.
- Copy tender-checkout.tsx and card-utils.ts and tender-checkout.css into components/sundae/.
- The CSS file is imported by the component, so no extra configuration is needed.
- Import the component and use it as shown below.
Usage
import { TenderCheckout } from '@/components/sundae/tender-checkout'
<TenderCheckout
amount={4800}
currency="USD"
description="Field notebook, set of 3"
onPay={async (card) => {
const res = await fetch('/api/pay', { method: 'POST', body: JSON.stringify({ token: await tokenize(card) }) })
if (!res.ok) throw new Error('Your card was declined.')
}}
/>Props
| Prop | Type | Default |
|---|---|---|
amount*Amount in minor units, for example 4800 for $48.00. | number | — |
currencyISO 4217 currency code. | string | 'USD' |
localeLocale for price formatting. Defaults to the browser locale. | string | — |
descriptionOrder summary line shown above the fields. | string | — |
onPay*Called with validated details. Resolve for success, or throw an Error to show its message. | (card: CardDetails) => Promise<void> | — |
submitLabelOverrides “Pay $48.00”. | string | — |
noteHelper text under the button in the idle state. | string | — |
Accessibility
- Every field is a labelled native input with the right autocomplete tokens (cc-number, cc-exp, cc-csc, cc-name), so browser and password-manager autofill work.
- Errors are linked with aria-describedby and appear on blur, not on every keystroke.
- Payment status is announced through a polite live region. The decorative card is aria-hidden.
- With prefers-reduced-motion, the flip and progress animations are instant.
Customization
- Restyle the card face with --t-card, --t-card-ink and --t-gold.
- card-utils.ts exports detectBrand, luhn, validExpiry and the formatters for use in your own forms.
- Never send raw card numbers to your own server. Tokenize with your payment provider first.
FAQ
- Is Tender Checkout free?
- Tender Checkout is part of Sundae Pro. The live preview is open to everyone; the source installs with a Pro account.
- How do I install Tender Checkout?
- Run npx shadcn@latest add https://sundaeui.dev/r/tender-checkout.json. The files are written to components/sundae/. It has no dependencies beyond React 18 or later.
- Does Tender Checkout support keyboard and screen readers?
- Every field is a labelled native input with the right autocomplete tokens (cc-number, cc-exp, cc-csc, cc-name), so browser and password-manager autofill work. Errors are linked with aria-describedby and appear on blur, not on every keystroke.
- Does Tender Checkout respect reduced motion?
- Yes. With prefers-reduced-motion set, decorative motion is removed and state changes are instant.
More like this
All checkout →- Pro
Seat Checkout
Checkout · Understated - Pro
Payment Methods
Checkout · Refined - Pro
Onboarding Flow
Forms · Refined - Pro
Currency Swap
Checkout · Refined Coupon Field
Checkout · Refined- Pro
Tip Selector
Checkout · Refined