Fatura Takip
01 — The brief
Fatura Takip is a finance panel for agencies and freelance teams. Money is tracked the way the work actually happens: a customer signs an engagement, an engagement holds projects, and a project earns through a payment plan — often split, like 30% up front and 70% on delivery. The panel keeps every installment, marks what has been invoiced, tracks expenses and subscriptions, and answers the one question that matters at month end: did this make money?
The hard part isn’t the forms — it’s correctness. A partial payment has to move an installment’s status on its own; invoiced and uninvoiced amounts must never disagree; and one workspace’s numbers must never bleed into another’s.
02 — Approach
Fatura Takip is a Next.js app on the App Router: server components read data, server actions perform every mutation, and Supabase provides Postgres, auth and storage. A lot of the logic a typical app keeps in the API layer lives in the database instead — triggers keep a payment plan’s status in sync as partial payments land, and a wall of reporting views compute monthly summaries, profit-and-loss and per-project profitability in SQL rather than in application code.
Isolation is enforced by Postgres row-level security, so a workspace can only ever see its own rows, with capability-based roles on top for who can do what. Billing runs through Lemon Squeezy as merchant-of-record, with an HMAC-verified webhook and idempotent handling of the subscription lifecycle. Notifications go out over Resend email and web push, driven by Cloudflare Cron and pg_cron; and money can be shared read-only with a client through a revocable, rate-limited portal link.
- Framework Next.js (App Router) · server components + server actions
- Data Supabase Postgres · triggers + reporting views · RLS
- Correctness DB triggers sync plan status · invoiced/uninvoiced in lockstep
- Billing Lemon Squeezy (merchant-of-record) · HMAC webhook · idempotent
- Notifications Resend email · web push (VAPID) · Cloudflare Cron + pg_cron
- Extras Multi-currency FX · revocable client portal · PWA · Sentry
None of this was free. Each choice bought something and charged for it somewhere else — the work was in choosing where to pay.
-
Trade-off 01
Logic in the database over logic in the app
Putting status triggers and reporting views in Postgres means the numbers are computed once, close to the data, and can’t drift between code paths. The cost is that business rules now live partly in SQL migrations — a less familiar place to read and test them than TypeScript.
-
Trade-off 02
Row-level security over app-side checks
Enforcing tenant isolation in the database means a forgotten WHERE clause can’t leak another workspace’s money — the boundary holds even if the app is wrong. The cost is that every access path has to be designed with RLS in mind, and policies are their own thing to test.
-
Trade-off 03
Lemon Squeezy over a direct gateway
A merchant-of-record handles tax, invoicing and the subscription lifecycle — a lot to not build. The cost is a webhook contract to verify and reconcile idempotently, and less control than integrating a raw payment API: a deliberate swap of control for scope, after an earlier Iyzico integration was removed.
-
Trade-off 04
Browser-printed reports over a PDF service
Yearly and monthly reports are laid out in the app and printed by the browser, so there’s no headless-Chrome service to run or pay for. The cost is that the output is a print stylesheet’s job, not a pixel-perfect server-rendered PDF.
Invoiced and uninvoiced should never be able to disagree.
03 — Results
Fatura Takip is live at faturatakip.app. It runs on forty-seven database migrations, a couple of dozen server-action modules, and a test suite that checks money correctness, tenant isolation and accessibility on every push. Agencies track engagements, split payments and expenses in it, and get monthly profit-and-loss without a spreadsheet.
04 — What’s next
With the money model and billing solid, the next work is depth: richer reporting and profitability views, more of the recurring-cost automation, and continuing to widen the accessibility and test coverage that already gate every release.