
Payify: Designing a Billing System That Actually Makes Sense
The Problem with Invoicing Tools
After years of freelancing in Germany, I've tried nearly every invoicing tool on the market. Lexoffice, sevDesk, Fastbill, even spreadsheets with PDF exports. Each had the same problem: they were either built for enterprises with features I'd never use, or so stripped-down they couldn't handle basic German tax requirements like the Kleinunternehmerregelung.
The breaking point came when I needed to track a simple invoice through its lifecycle — draft, sent, viewed, paid — and realized I was juggling between three different tools. The invoice was in one app, the email confirmation in another, and my mental model of what was actually happening existed only in my head.
That's when I decided to build Payify: an invoicing system designed specifically for how I work, not how enterprise software vendors think I should work.
Starting with the Mental Model
Before writing any code, I spent two weeks with pen and paper. Not wireframing — thinking. What does an invoice actually represent? It's not just a PDF. It's a promise, a record, and a communication tool all at once. I needed to capture that reality in the interface.
The core insight was treating invoices as entities with distinct states, not just documents. An invoice isn't just 'paid' or 'unpaid' — it moves through a clear progression: Draft (being written), Released (finalized but not sent), Sent (delivered to client), Paid (money received). Each state triggers different actions and displays different information.
This state-machine thinking influenced every design decision that followed. The dashboard doesn't just show a list of invoices — it groups them by state, making the next action immediately obvious.
The Dashboard: Information Architecture
The dashboard needed to answer three questions instantly: How much money is outstanding? What needs my attention today? What's the overall health of my freelance business? I experimented with dozens of layouts before landing on a three-column approach.
The left column shows monetary metrics: total outstanding, overdue amount, and this month's revenue. These numbers are always visible because they represent the core reason the tool exists. The center shows invoices requiring action, sorted by urgency. The right column provides quick actions for common tasks.
Color plays a subtle but crucial role. I avoided the trap of making overdue items screaming red — that creates anxiety, not action. Instead, overdue invoices use a muted amber that draws attention without causing panic. The goal is to inform, not alarm.
Invoice Creation: The Form That Writes Itself
Most invoicing tools treat form creation as data entry. You fill in fields, click save, done. But invoice creation is actually a design task — you're crafting a document that represents your professionalism. The form should feel like that.
I built the invoice editor as a live preview system. As you fill in line items, the right side of the screen shows exactly how the PDF will look. No more 'create draft, download PDF, notice mistakes, edit, repeat.' You see what you're getting as you build it.
Line items use an inline editing pattern inspired by spreadsheets. Tab moves between fields, Enter creates a new line, and the interface stays out of your way. For recurring items, there's a quick-add menu with saved templates.
// Line item component with inline editing
<LineItem
onTab={() => focusNextField()}
onEnter={() => addNewLine()}
onBlur={() => recalculateTotal()}
>
<EditableCell field="description" />
<EditableCell field="quantity" type="number" />
<EditableCell field="rate" type="currency" />
<ReadOnlyCell value={quantity * rate} />
</LineItem>The Status Tracking System
Invoice status isn't just metadata — it's the core of the user experience. I designed the status system to be both visible and actionable. Each status has a distinct color, icon, and set of available actions.
- Draft (gray): Edit, Delete, Release
- Released (blue): Edit, Send, Download PDF
- Sent (purple): Resend, Mark as Viewed, Mark as Paid
- Paid (green): Download, Archive
- Overdue (amber): Send Reminder, Mark as Paid
Status transitions are protected by business logic. You can't mark an invoice as paid if it was never sent. You can't edit a paid invoice (you'd need to create a credit note instead). These constraints aren't arbitrary — they reflect how accounting actually works.
type InvoiceStatus = 'draft' | 'released' | 'sent' | 'paid' | 'overdue'
const statusTransitions: Record<InvoiceStatus, InvoiceStatus[]> = {
draft: ['released'],
released: ['sent', 'draft'],
sent: ['paid', 'overdue'],
paid: [],
overdue: ['paid'],
}
function canTransition(from: InvoiceStatus, to: InvoiceStatus): boolean {
return statusTransitions[from].includes(to)
}Email Automation: React Email in Action
Sending invoices by email seems simple until you actually build it. The email needs to look professional, include the PDF attachment, and contain just enough context without being overwhelming.
// React Email template for invoice notification
export const InvoiceEmail = ({ invoice, status }: Props) => (
<Html>
<Head />
<Preview>Invoice #{invoice.number} - {formatCurrency(invoice.total)}</Preview>
<Body style={main}>
<Container>
<Heading>Invoice #{invoice.number}</Heading>
<Text>Amount due: {formatCurrency(invoice.total)}</Text>
<Text>Due date: {formatDate(invoice.dueDate)}</Text>
<Button href={invoice.viewUrl}>View Invoice</Button>
</Container>
</Body>
</Html>
)Each status transition can trigger an email automatically. When an invoice moves to 'Sent', the client receives it. When it becomes overdue, a gentle reminder goes out. The system handles the communication so I don't have to remember.
Component Design Philosophy
Every component in Payify follows a few core principles. First, components should be self-documenting — their purpose should be clear from their visual design without needing labels. Second, interactive elements should provide immediate feedback. Third, empty states should guide users toward action.
The invoice card component is a good example. It shows client name, amount, date, and status in a compact format. Hover reveals a subtle shadow and the action buttons. Click anywhere opens the detail view. Right-click shows a context menu.
Empty states were designed with as much care as full states. An empty invoice list doesn't just say 'No invoices' — it shows a friendly illustration and a prominent 'Create your first invoice' button.
The PDF Generation Pipeline
PDF generation is one of those features that seems simple but hides significant complexity. The PDF needs to be pixel-perfect, consistently rendered across all systems, and generated quickly enough to not break the UX flow.
I chose React PDF for its developer experience — designing PDFs with React components is dramatically more pleasant than the alternatives. The invoice template is a separate React tree that renders to PDF, sharing styling constants with the web app for consistency.
What I Learned
Building Payify taught me that 'simple' tools are the hardest to design. Every feature needs to justify its existence. Every screen needs to respect the user's time. Every interaction needs to feel natural.
The most valuable design decisions were often the ones about what not to include. No complex reporting module — export to CSV and use a spreadsheet if you need analytics. No multi-currency support — I work in Euros, the tool works in Euros. No collaboration features — this is a solo freelancer tool.
Constraints liberate design. By accepting a narrow use case, I could optimize every detail for that case. The result is software that feels like it was made for me — because it was.
What's Next
Payify is actively evolving. Current priorities include a quote/proposal module that converts to invoices, expense tracking for tax preparation, and deeper banking integration for automatic payment matching. The foundation is solid; now it's about expanding capability without losing focus.

RIFT: Building a Pixel-Glass Design System for File Transfer
How gaming aesthetics transformed a file transfer tool — from dithered gradients to gaming terminology as UX.

Einfachrecht: Building an Enterprise CMS for Legal Content
How I architected a content management system for legal information — from editorial workflows to SEO optimization.