Skip to main content
← All notes
Building

Invoices come straight from Stripe, not a database

software

The portal does not store invoice records in its own database. When a client opens the invoices page, the server hits the Stripe API with their stripeCustomerId and pulls back every payment charge. The InvoiceTable component receives that array and renders it as an accessible HTML table with proper scope attributes on every column header and an aria-label on the table itself. Amounts arrive in pence from Stripe. The formatCurrency function uses Intl.NumberFormat with the invoice's actual currency code so a GBP payment shows a pound sign and a USD payment shows a dollar sign. No hardcoded currency symbols, no manual conversion. The status column uses the same colour language as the rest of the portal — emerald for paid, orange for pending, red for failed. Each status maps to a badge class and a display label through two Record objects. Adding a new status from Stripe means adding one entry to each record. The receipt column links directly to Stripe's hosted receipt page. Each link has an aria-label that includes the invoice description so a screen reader user knows which receipt they are opening. On mobile screens below 640 pixels, the date column hides entirely using sm:table-cell. The table fits on a phone without horizontal scrolling because the least critical column disappears first. One component, one Stripe API call, one Intl formatter, and every client sees their full payment history without me manually entering a single invoice.

Comments coming soon

Sign in with TikTok to leave a comment. Coming soon.