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

Invoices Laravel Package

consoletvs/invoices

Laravel package for generating professional PDF invoices with customizable templates, line items, taxes, discounts, shipping, and currency support. A fluent API makes it easy to build, preview, and download invoices.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install the package and its PDF driver. The package renders HTML through a driver, so you also need either mpdf/mpdf or dompdf/dompdf (check the README for the version constraint your PHP/Laravel supports):
composer require consoletvs/invoices
php artisan vendor:publish --provider="ConsoleTVs\Invoices\InvoicesServiceProvider"
  1. Read config/invoices.php first. This is where the whole package is configured: the default template, the driver (mpdf or dompdf), the default currency symbol/code, and the disk used for saving. Everything else in the package is a thin builder over these values.

  2. Learn the three classes you'll actually touch: ConsoleTVs\Invoices\Classes\Invoice, Buyer, and Seller. The Invoice is a fluent builder; Buyer/Seller are simple value objects for the party block.

  3. Generate your first invoice from a controller. This is the canonical "hello world":

use ConsoleTVs\Invoices\Classes\Invoice;
use ConsoleTVs\Invoices\Classes\Buyer;
use ConsoleTVs\Invoices\Classes\Seller;

$invoice = (new Invoice)
    ->addItem('Consulting hours', 90, 3)
    ->setDiscount(10)
    ->setCurrency('EUR', '€')
    ->setSerial('INV-2024-001')
    ->setDate('2024-03-01')
    ->setDueDate('2024-03-31')
    ->setSeller(new Seller('Acme Ltd', '1 Main St', '+1 555 0100', '[email protected]'))
    ->setBuyer(new Buyer('Jane Doe', '42 Oak Ave', '+1 555 0199', '[email protected]'));

return $invoice->download('invoice.pdf');   // or ->show() / ->save($path)

If the exact fluent method names differ in your installed major version (the API has shifted between releases), open src/Classes/Invoice.php — every setter is a few lines and self-documenting. The facade Invoices::make(...) is a convenience wrapper around the same builder.

First real use case: a GET /invoices/{invoice}/pdf route that rebuilds an invoice from a database record and streams it. Start there rather than trying to build a full billing system on day one — this package is a renderer, not a billing engine.

Implementation Patterns

Persist data, not just PDFs. The single most important pattern: store the invoice (serial, buyer details, line items, totals, currency, dates) in your own tables, and treat the package as a view layer. Regenerating on demand from the record lets you fix a template or a typo and reissue without a migration. Store the generated file only as a cache.

Wrap the builder in a service or factory. Build the seller once from config and centralise serial/date formatting:

class InvoiceFactory
{
    public function fromRecord(InvoiceRecord $record): Invoice
    {
        $invoice = (new Invoice)
            ->setSerial($record->serial)
            ->setDate($record->issued_at->format('Y-m-d'))
            ->setDueDate($record->due_at->format('Y-m-d'))
            ->setCurrency($record->currency, $record->currency_symbol)
            ->setSeller(new Seller(
                config('company.name'),
                config('company.address'),
                config('company.phone'),
                config('company.email'),
            ))
            ->setBuyer(new Buyer($record->buyer_name, $record->buyer_address, $record->buyer_phone, $record->buyer_email));

        foreach ($record->items as $item) {
            $invoice->addItem($item->description, $item->unit_price, $item->quantity);
        }

        return $invoice->setDiscount($record->discount);
    }
}

Every controller, job, and mailer then calls $factory->fromRecord($record)->download(), and your template logic lives in one place.

Archive to disk and serve from a signed route. Generate once, save to an explicit path, store the path, and expose it via a short-lived URL::temporarySignedRoute(...) instead of public storage. Keeps customer invoices non-guessable:

$path = storage_path("app/invoices/{$record->serial}.pdf");
$factory->fromRecord($record)->save($path);

Mail as an attachment. Generate to a temp file and attach it with Laravel's mailable, rather than trying to stream binary through the mailer:

Mail::to($record->buyer_email)->send(new InvoiceIssued($record, $path));

Queue bulk generation. PDF rendering (especially mPDF) is CPU-bound and memory-hungry. Month-end "generate all invoices" jobs should be queued with a per-job timeout, and ideally chunked so one bad invoice doesn't fail the batch.

Customise templates by publishing views. The package ships Blade templates; publish them (see the README's publish tag) and edit the HTML/CSS. If you need more than one look, either publish and select a template per invoice via the builder/setter, or copy the package template into your own namespace and render it yourself — it's just a Blade file fed the invoice data.

Test with the download assertion. In feature tests, assert the response is a PDF (assertDownload('invoice.pdf') where supported, or check the Content-Type and a %PDF prefix) and assert on your own factory output rather than parsing the binary.

Gotchas and Tips

  • No driver = no PDF. The most common "it just returns HTML/blank" symptom is a missing or misconfigured PDF backend. Confirm mpdf/mpdf or dompdf/dompdf is installed and that driver in config/invoices.php matches. Run php artisan config:clear after editing it — a cached config silently ignores your change.
  • Images need filesystem paths, not URLs. For logos under dompdf especially, use an absolute path (public_path('logo.png')) or a data URI. Remote URLs are frequently blocked or silently dropped.
  • Currency and dates are strings you format. The package does not do locale-aware money formatting or date rendering; whatever you pass in is what appears. Format with your own helper and be consistent — mixing 1.234,56 and 1,234.56 across invoices is a real reconciliation problem.
  • The package enforces nothing about serials. Uniqueness, sequential numbering, gaps — all your responsibility via a DB unique index or a dedicated sequence table. Never rely on array order or timestamps as invoice numbers.
  • It's not a tax engine. addItem/setDiscount cover a straightforward line-item model; VAT rules, per-line tax rates, partial payments, and credit notes are on you. Model those in your schema and either pre-compute totals before passing them in or extend the template.
  • Long descriptions break layouts. Templates wrap text poorly at extreme lengths. Set sensible column limits, and test a 20-line invoice — pagination behaviour differs between mPDF and dompdf, so test with the driver you'll ship.
  • Blade escaping. If buyer/seller/item text comes from users, make sure the templates escape it ({{ }}, not {!! !!}). Custom published templates are the usual place HTML injection sneaks in.
  • Memory and timeouts. mPDF in particular can exceed default memory_limit on large invoices. Raise the limit for the generation job or run it in a queued worker with its own limits; don't let a PDF render take down a web request.
  • Maintenance risk. The last release is early 2024 and the project is stable rather than actively evolving. Pin the version, keep the generated PDFs and the source records so you can migrate off cleanly (any HTML-to-PDF pipeline can replace it since the templates are just Blade), and don't build bespoke features on top of internals — stick to the builder API and published views.
  • Extension points are narrow, by design. You get the builder, the templates, and the config. If you need a different renderer (Puppeteer, Chromium, Snappy), it's usually cleaner to render the same Blade template yourself than to fight the driver abstraction.
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.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment