EU freelancer invoicing + time tracking SaaS built with Laravel 12, Livewire 4, and PostgreSQL.
InvoiceKit helps EU-based freelancers manage clients, track billable time, generate VAT-compliant invoices, export Peppol/UBL XML, and manage subscriptions via Stripe. It handles the complexity of EU VAT rules — reverse charge, OSS, and small-business exemptions — automatically.
- Time Tracking — Live timer, manual entry, weekly summary, per-project earnings
- Client Management — EU VAT number validation, per-client currency and language defaults, company auto-fill by VAT/registration number (VIES + Gemini AI fallback)
- Project Management — Hourly rate, multi-currency, archive/restore
- EU VAT Engine — Standard rate, reverse charge (B2B), OSS (B2C), exempt (non-EU), small-business exemption for all 27 EU countries
- Invoice Generation — PDF (DomPDF), UBL 2.1 / Peppol BIS Billing 3.0 XML, sequential numbering, full lifecycle
- Recurring Invoices — Weekly / monthly / quarterly / yearly auto-generation
- Client Portal — Tokenised public invoice links (no login required)
- Expense Tracking — Receipt upload, CSV export, category filters
- Billing — Stripe Checkout + Customer Portal, webhook-driven subscription sync, 14-day Pro trial
- AI Document Import — Drag-and-drop invoices and receipts (PDF/JPG/PNG); Gemini 1.5 Flash extracts all fields; review and confirm to create a draft invoice or expense with receipt attached
- Notifications — In-app bell, web push (VAPID), invoice reminder emails with PDF attachment
- Multi-currency — EUR, USD, BGN, RON, PLN, CZK, HUF
- 24 EU languages — Full UI and PDF localisation
| Layer | Technology |
|---|---|
| Framework | Laravel 12 |
| Frontend | Livewire 4 + Alpine.js + Tailwind CSS 3 |
| Database | PostgreSQL 16 |
| Cache / Queue | Redis 7 |
| File Storage | MinIO (S3-compatible) |
| barryvdh/laravel-dompdf | |
| Payments | Stripe (raw stripe/stripe-php v19) |
| Container | Docker (PHP-FPM + Nginx) |
- Docker Desktop (or Docker Engine + Compose)
- Node.js ≥ 20 + npm (for frontend assets)
git clone https://github.com/your-org/invoicekit.git
cd invoicekit
cp .env.example .envEdit .env and set at minimum:
APP_URL=http://localhost:8008
# Stripe (optional for local — leave blank to skip billing features)
STRIPE_SECRET_KEY=sk_test_...
STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
STRIPE_PRO_PRICE_ID=price_...
STRIPE_STARTER_PRICE_ID=price_...
# Web push — subscriber contact email (keys are generated in Step 4)
VAPID_SUBSCRIBER_EMAIL=hello@yourdomain.comAll other defaults work out of the box with Docker.
docker compose up -dThis starts: app (PHP-FPM), nginx (port 8008), postgres (port 5432), redis (port 6379), minio (port 9002, console 9003), and worker (queue).
docker compose exec app composer install
docker compose exec app php artisan key:generate
docker compose exec app php artisan migrate --seeddocker compose exec app php artisan webpush:vapidThis auto-writes VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY into your .env file.
Tip: Notification reminder emails are sent to the queue and written to
storage/logs/laravel.logby default (MAIL_MAILER=log). Rundocker compose logs -f workerto watch job processing.
docker compose exec app php artisan storage:minio-initOr manually: open the MinIO console at http://localhost:9003 (user invoicekit / password secret123456), create a bucket named invoicekit, and set its policy to public.
npm install
npm run buildFor hot-reload during development, run npm run dev in a separate terminal (or use composer run dev to start all processes together).
Visit http://localhost:8008 and register an account. The onboarding wizard runs on first login.
# All feature tests
docker compose exec app php artisan test --compact tests/Feature/
# Single file
docker compose exec app php artisan test --compact tests/Feature/InvoiceTest.php
# Filter by name
docker compose exec app php artisan test --compact --filter=test_user_can_create_invoiceTests use an in-memory SQLite database and do not touch the development PostgreSQL instance.
| Command | Purpose |
|---|---|
php artisan tinker |
Interactive REPL in app context |
php artisan route:list --except-vendor |
List all application routes |
php artisan queue:work |
Process queued jobs (worker container handles this in Docker) |
php artisan webpush:vapid |
Generate VAPID keys for web push notifications |
vendor/bin/pint |
Fix PHP code style |
composer run dev |
Start server + queue + logs + Vite concurrently |
Use the Stripe CLI to forward webhook events to your local instance:
stripe listen --forward-to http://localhost:8008/billing/webhookCopy the printed signing secret into .env as STRIPE_WEBHOOK_SECRET.
See .env.example for all variables. Key groups:
| Group | Variables |
|---|---|
| App | APP_KEY, APP_URL, APP_CURRENCY |
| Database | DB_HOST, DB_DATABASE, DB_USERNAME, DB_PASSWORD |
| Stripe | STRIPE_SECRET_KEY, STRIPE_PUBLISHABLE_KEY, STRIPE_WEBHOOK_SECRET, STRIPE_PRO_PRICE_ID, STRIPE_STARTER_PRICE_ID |
| MinIO | MINIO_ACCESS_KEY, MINIO_SECRET_KEY, MINIO_ENDPOINT, MINIO_BUCKET, MINIO_PUBLIC_URL |
| Web Push | VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBSCRIBER_EMAIL |
MAIL_MAILER, MAIL_HOST, MAIL_FROM_ADDRESS |
| Scenario | VAT Treatment |
|---|---|
| Same country | Local VAT rate |
| EU B2B — buyer has VAT number | Reverse charge (0%) |
| EU B2C — buyer has no VAT number | Seller's rate (OSS) |
| Non-EU buyer | Exempt (0%) |
| Seller is VAT-exempt small business | Exempt (0%) with legal notice |
Supported rates: BG 20% · DE 19% · FR 20% · RO 19% · PL 23% · CZ 21% · IT 22% · ES 21% · NL 21% · PT 23% · AT 20% · BE 21% · HR 25% · HU 27% · SE 25%
See FEATURES.md for a detailed feature reference and DEPLOY.md for production/staging deployment on Kubernetes.
MIT