Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Invoices Laravel Package

saasykit/laravel-invoices

A Laravel package for creating and managing invoices, with models, migrations, line items, totals, and a straightforward API for adding invoice generation to your app's billing flows.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check first. Documentation for this package is thin — the repo README, src/, config/, and the shipped Blade views are the documentation. Budget the first hour for reading source, not tutorials. Class/namespace names below follow the package's stated API shape; confirm exact names against the README and src/ before pasting anything into your codebase.

Install and publish (the 10-minute path):

  1. composer require saasykit/laravel-invoices
  2. Check for a publish command in the README/provider: typically php artisan vendor:publish --provider="SaaSykit\LaravelInvoices\LaravelInvoicesServiceProvider" with tags for config and views.
  3. If the package ships migrations (likely, given "Eloquent-friendly models"), run php artisan migrate and confirm the invoice/line-item tables landed.
  4. Skim config/laravel-invoices.php — numbering format, storage disk, currency/locale, default template, tax defaults. This file is where 80% of your future configuration questions are answered.

Where to look first, in order:

File Why it matters
src/*ServiceProvider.php Container bindings, singletons (PDF renderer, builder), publishable paths — tells you what's injectable vs. facade-only.
config/laravel-invoices.php Numbering, disk, currency, template paths, PDF driver options.
resources/views/ (published) The invoice layout you'll be editing. This is your real template contract.
src/Models/ The Eloquent models — relations, fillable, casts. Confirms how to attach invoices to your billable entities.

First use case — render a one-off PDF without touching the database:

use SaaSykit\LaravelInvoices\Facades\Invoice; // confirm facade name

$pdf = Invoice::make()
    ->number('INV-2026-0001')
    ->currency('USD')
    ->from([...])           // seller details
    ->to([...])             // customer details
    ->addItem('Pro plan', 49.00, qty: 1)
    ->addItem('Seats x3', 9.00, qty: 3)
    ->tax(20)               // or per-item tax, check README
    ->render();             // HTML or PDF depending on API

return $pdf->download('invoice.pdf');

Start here, not with persistence. Rendering is the core capability and the fastest way to see whether the shipped template and PDF engine meet your design requirements. Only after the layout is acceptable is it worth wiring persistence and numbering.

Implementation Patterns

1. Separate "build" from "persist" from "render." The builder is fluent and stateful; the model is the durable record; the renderer is the side effect. Treat them as three steps in your own service class, not as one chained call inside a controller. That way a failed PDF render doesn't leave a half-created invoice row, and you can re-render historical invoices from stored data without replaying business logic.

2. Snapshot everything at issue time. This is the single most important pattern for invoices as legal/financial documents: when the invoice is finalized, copy seller details, customer details, line-item prices, tax rates, and totals into the invoice record — do not join back to users, plans, or prices at render time. Plans get renamed, addresses change, tax rates move. The invoice must render identically in three years. Treat the builder input as immutable once status = issued.

3. Attach to any billable via a polymorphic relation. The description explicitly targets "SaaS billing flows" and "attach to any billable entity." Add a morphMany on both User and Team/Workspace (or whatever your tenants are) rather than a user_id column. This avoids a migration the day you add team billing.

4. Numbering belongs in the database, not the config. Config gives you a format string; you still need atomicity. Generate numbers inside a transaction with a dedicated sequence table or lockForUpdate() on a counter row — never Invoice::max('number') + 1. Two concurrent checkout webhooks under a unique index will teach you this the hard way.

5. Two rendering paths, deliberately.

  • Streamed/download on demand for "user clicks Download PDF" — render fresh, no storage.
  • Stored at issue time for anything you email, archive, or must legally retain — render once, put the PDF on the configured disk, then serve a signed URL. Storing the bytes freezes the template version too, which protects you when someone edits the Blade layout.

6. Queue bulk and recurring generation. Monthly invoice runs are CPU-bound on the PDF engine. Dispatch one job per invoice, store the file, and batch the emails separately. Don't render 500 PDFs in a single request; memory will find you.

7. Email via the mailer, but attach from disk. The description mentions mailer integration. If the package offers an InvoiceMail, prefer the pattern "store the PDF, then attach the stored file by path" over "render, then attach the in-memory string." It survives retries, keeps job payloads small, and lets you re-send without re-rendering.

8. Override templates by publishing, not by editing vendor. Publish the views, edit the copies, and pin the template path in config. Keep a diff of the shipped views per version so upgrades don't silently overwrite your branding.

9. Test with contract assertions, not just "it returns a PDF." Assert on the rendered content — assertSee('INV-2026-0001'), assertSee('$49.00'), totals line, tax line — and assert the PDF is non-empty and starts with %PDF. A package that "works" can still produce a blank page or wrong totals after a dependency bump.

10. Preview HTML in a route. Expose a dev-only route that renders the HTML variant of the invoice for the current user. It's the fastest debugging tool you'll have, and it's the same code path as the PDF minus the engine.

Gotchas and Tips

  • GPL-3.0 is the headline risk. This is a copyleft license, and the package targets SaaS billing flows. Shipping it inside a closed-source commercial SaaS is a legal question, not an engineering one — get it cleared before you build on it, or plan to vendor/fork the concepts and write your own renderer. This package will not have the usual permissive-license freedom you assume from Laravel packages.
  • Pre-2.0 API. At 1.1.0 the public surface can change in minor releases. Wrap the builder in your own InvoiceService with 3–4 methods you control. When the package renames addItem to lineItem, you edit one file instead of forty.
  • "Laravel 13 support" is a declared constraint, not proof of CI coverage. Resolve in a scratch branch first, run your own invoice test suite, and check composer.json to see whether L11/L12 constraints were widened or dropped — dropping them strands you mid-migration.
  • Money handling. Packages in this space vary between floats, cents-as-integers, and a money value object. Find out which before you write tax logic. Float arithmetic on line items produces cent-level drift that shows up as an off-by-one on the invoice total — and finance will notice. Round once, at the total, and record the rounding rule.
  • Tax rounding and per-line vs. per-invoice tax are the two questions that generate the most support tickets in any invoicing library. Test a 3-line invoice with a 7.5% rate and compare against a spreadsheet before shipping.
  • PDF engine font support. If it's Dompdf under the hood, custom fonts require registering font files and can blow memory on large invoices. Emoji and unusual glyphs render as boxes; CJK needs an explicit font. Check the engine in composer.json (dompdf/dompdf, mpdf/mpdf, etc.) and set memory_limit on the queue worker accordingly.
  • Numbering races. Covered above, but worth repeating: config-driven numbering with a {year} or {sequence} placeholder is not concurrency-safe on its own.
  • Published config drift. After any package upgrade, diff your published config/laravel-invoices.php against the shipped default. New keys with sane defaults are fine; renamed keys fail silently as null. Same for published views.
  • Storage disk config. The default disk in a fresh Laravel app is local, which is not public and may not survive container redeploys on ephemeral filesystems. Explicitly set the invoice disk (S3 or a mounted volume) in config; don't rely on the default.
  • Timezone and date format. Invoice date and due date should be stored as dates, formatted at render time from config, and generated in the issuer's timezone — not the server's UTC. An invoice dated "yesterday" because of a midnight boundary is a support ticket every month.
  • Debugging a broken PDF: in order — (1) render the HTML variant in the browser; (2) if HTML looks right and PDF doesn't, it's the engine (fonts, memory, CSS support like flexbox/grid which Dompdf lacks); (3) if HTML is wrong, it's your data or the published template; (4) if it throws, wrap the render call in a try/catch and log the builder state before rendering.
  • CSS support is limited in most PHP PDF engines. Use table-based layouts and inline styles in the invoice template; modern CSS will render correctly in your HTML preview and break in the PDF. This is the most common "it worked in dev" surprise with invoice packages.
  • Extension points to prefer: published Blade views, the config file, and a subclassed model (adding relations/methods) — not overriding internal classes. The former survive minor upgrades; the latter don't.
  • Queue payload compatibility across upgrades. If the package dispatches jobs, drain workers before deploying a version bump. Class renames across a package upgrade strand in-flight jobs.
  • Single maintainer, 7 stars. Low bus factor. Keep the fork option open: the value is in the Blade template and the fluent API shape, both of which are cheap to reimplement if the package stalls. Do not let invoice generation become a hard dependency you can't replace in a week.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console