offline-agency/laravel-fatture-in-cloud
Laravel integration for the Fatture in Cloud API, making it easy to manage invoices, clients, products, and other billing data from your PHP applications. It provides a configurable API client, fluent resource methods, and Laravel-friendly configuration for streamlined Italian e-invoicing workflows.
composer require offline-agency/laravel-fatture-in-cloud
The package auto-registers its service provider on modern Laravel; publish the config first and read it end-to-end before writing any code:
php artisan vendor:publish --tag="fatture-in-cloud-config"
Set the two values almost every call depends on in .env:
FATTURE_IN_CLOUD_ACCESS_TOKEN=...
FATTURE_IN_CLOUD_COMPANY_ID=...
The access token is an OAuth2 bearer token obtained from the Fatture in Cloud dashboard / your own OAuth flow — this package gives you a client, not a login flow. Get a token for a sandbox company first (Fatture in Cloud exposes a test environment) so you can poke at endpoints without touching real invoices.
Look at the following, in order: the published config file (base URL, token, company id), the package's main client/Facade class, then the resource-specific classes (invoices, clients, products, suppliers). Your first real use case is nearly always "list the invoices for company X and page through them" — do that before attempting a create/update, because list responses double as your schema reference for the model fields the API actually expects.
Resolve the client, don't new it. Use the container/Facade so config and HTTP client (with its base URL and auth headers) are wired for you. Inject it into a dedicated service class rather than calling it straight from controllers — Fatture in Cloud payloads are verbose and you don't want them scattered across your app.
Wrap the API in your own gateway. Create an InvoiceGateway / ClientGateway in your app that consumes this package and returns your domain models or DTOs. This keeps the vendor's method names, argument order and pagination shape out of your business logic, which matters because these wrapper packages change method signatures between minor versions more often than you'd like.
Treat every write as a queued job. Invoicing calls are slow, rate-limited and occasionally flaky. Push CreateInvoice, SendInvoice, UploadAttachment etc. onto a queue with tries, backoff and failed() handling. Idempotency is your responsibility — check for an existing document (by your own external reference / numeration) before re-creating on retry.
Pagination: these clients typically expose page / per_page and a last_page in the response envelope. Loop until the current page equals last page, and put the loop inside a job that can resume from a stored page number rather than in a request cycle.
Testing: bind a fake in your test suite (swap the client in the container, or Http::fake() the Fatture in Cloud host) and assert on the request payloads your gateway produces. Golden-file the JSON for invoice creation — the API validates field names and types strictly, and a silent typo shows up as a 422 at runtime, not at compile time.
Multi-company: if you serve several legal entities, don't rely on the single company_id in config. Instantiate/configure a client per company inside your gateway and resolve it by a tenant key; a config-only company id is a trap as soon as you onboard a second client.
^2.2.8 in composer.json if you're on 8.1 so you don't get downgraded by a transitive constraint. It's a safe patch-level upgrade — no breaking changes flagged.access_token + refresh_token + expires_at, refresh in a middleware or before queued jobs run) or your nightly jobs will start 401-ing silently. Log 401s distinctly from 422s.company_id-scoped field). Don't catch a broad Exception and retry everything — retrying a 422 will just burn rate limit..env, and make sure your test suite can't accidentally hit the live host. This is the single cheapest way to avoid sending a real invoice to a real customer during development.Bus::batch(), add a small usleep/RateLimited job middleware, and check the response headers for remaining quota when debugging 429s.data, pagination, etc.); show/detail endpoints are shaped differently from list items. Write a small normaliser rather than assuming the create response looks like the list response.numeration, withholding). Do your decimal handling with a money library on your side; never do float arithmetic on these values and re-post them.timeout — the default can be long enough to tie up a queue worker.How can I help you explore Laravel packages today?