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 Fatture In Cloud Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

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.

Implementation Patterns

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.

Gotchas and Tips

  • PHP 8.1 (this release, 2.2.8). This is a compatibility bump, not a feature. Practically: if your app is on PHP 8.1 you can now install/upgrade without Composer resolver conflicts; if you were already on 8.0 nothing changes for you. Pin ^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.
  • Tokens expire. The package hands your bearer token to Guzzle; it does not manage refresh for you in the typical implementation. Build a token-refresh step (store 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.
  • 401 vs 403 vs 422 mean different things: 401 = expired/invalid token, 403 = token lacks scope for that company/entity, 422 = payload validation (usually a wrong field name or a missing company_id-scoped field). Don't catch a broad Exception and retry everything — retrying a 422 will just burn rate limit.
  • Sandbox before production. Point the base URL at the sandbox in non-production environments via .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.
  • Rate limits are per-minute and per-endpoint. Batch imports (e.g. seeding 500 clients) will trip them. Chunk with Bus::batch(), add a small usleep/RateLimited job middleware, and check the response headers for remaining quota when debugging 429s.
  • Response envelope nesting. List endpoints wrap results (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.
  • Numbers and money. Amounts come back as the API's own numeric types and Italian formatting conventions (VAT, numeration, withholding). Do your decimal handling with a money library on your side; never do float arithmetic on these values and re-post them.
  • Debugging: enable Guzzle's request/response logging (via the config's HTTP client options or a temporary handler) and dump the raw body — the package's exception messages often surface only the status code. Sanitise the token before logging.
  • Extension point: the config's Guzzle/HTTP client options are the easiest place to add retries, proxies, timeouts or a logging middleware without forking. Set a sane timeout — the default can be long enough to tie up a queue worker.
  • Watch the CHANGELOG. Maintainer is a single agency and releases are frequent and small; subscribe to releases and run your integration test suite (with fakes) on every bump, because "feat/add support" patches and small refactors can move method signatures.
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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager