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

Billingo Bundle Laravel Package

padam87/billingo-bundle

A Symfony bundle for integrating the Billingo invoicing API into your application. Create and manage invoices, partners, products, and PDF documents, with support for Hungarian and international invoices, tax rates, and electronic invoicing workflows.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle, not a Laravel package. There is no service provider, no auto-discovery, and no php artisan vendor:publish. Plan for a thin bridging layer rather than a drop-in install.

  1. composer require padam87/billingo-bundle (and note the underlying API client it pulls in — check composer.json).
  2. Get an API key from the Billingo dashboard (Settings → API). Put it in .env as BILLINGO_API_KEY; never commit it.
  3. Read, in this order:
    • README.md — usage examples and the expected config shape.
    • src/Resources/config/services.yaml — the real service IDs, constructor arguments, and which classes are public.
    • src/DependencyInjection/Configuration.php — every config key the bundle accepts (base URL, API key, HTTP client, serializer).
    • The wrapped client package — that's where the actual endpoints live.
  4. In Laravel, bind the client as a singleton in AppServiceProvider (or a dedicated BillingoServiceProvider), constructing it with config('services.billingo.key'). The bundle's PHP classes are framework-agnostic; only its DI extension is Symfony-specific, so you can use the client directly and ignore the bundle plumbing.
  5. First use case: a smoke test, not an invoice. Call a cheap GET — list partners, or fetch document blocks / tax codes — to confirm the key and base URL work. Then do partner lookup → create document → download PDF in that order.

The reference data you need before you can create an invoice (block IDs, tax codes, bank account IDs, currencies) all comes from the API. Fetch it once and cache it.

Implementation Patterns

Singleton client, injected everywhere. Build the client once in a service provider and resolve it via the container. Never construct it inline in a controller — you lose config caching and make testing hard.

Wrap it in your own service. A BillingoInvoiceService (or repository) around the raw client gives you one place to map your domain models to Billingo payloads, and one place to mock in tests. The bundle's API is generic; your app's is not.

Config-driven setup. Mirror the bundle's config keys into config/services.php:

'billingo' => [
    'key'      => env('BILLINGO_API_KEY'),
    'base_uri' => env('BILLINGO_BASE_URI', 'https://api.billingo.hu/v3'),
],

Always async for writes. Invoice creation and PDF download are external HTTP calls. Dispatch a queued job, store the returned document ID locally, and download the PDF in a second job (PDF generation can lag on Billingo's side). Never block a user request on it.

Cache reference data. Blocks, tax codes, banks, and currencies change rarely. Cache them with a TTL (hours, not forever) and refresh on error. Hardcoding IDs will break the first time your account is reconfigured.

Persist the Billingo IDs. Keep billingo_document_id and the assigned invoice number on your local invoice record. That's your only reliable lookup key for reconciliation, PDF retrieval, and cancellation.

Test with a fake HTTP layer. If the client accepts an injectable PSR-18/Symfony HttpClient, bind a mock in tests. Otherwise use Http::fake() at the transport level. Record real responses as fixtures — payload shapes are the thing you actually want to assert against.

Multi-tenant/reseller. If each tenant has its own Billingo account, make the client factory tenant-aware rather than a single singleton. Scope keys per tenant, not globally.

Gotchas and Tips

  • The Symfony/Laravel mismatch is the main trap. The bundle's DI extension never runs, so its config tree isn't validated and its parameter resolution (%env(...)%) doesn't happen. Validate your own config explicitly and fail loudly at boot if the key is missing.
  • Sandbox vs production. Verify the base URL before any test run. Creating a real invoice to "see if it works" is a financial record you cannot silently delete — you have to storno it, which itself is an API call.
  • Auth header quirks. Billingo uses an X-API-KEY header. A trailing space or newline from .env copying produces a 401 that looks like a key problem but isn't. trim() your env value.
  • Money is not a float. Send amounts as integers/strings in the smallest unit the API expects. Float rounding on tax lines will get invoices rejected or, worse, accepted with wrong totals.
  • Dates are YYYY-MM-DD. Don't pass Carbon objects or timestamps and hope the serializer handles it; format explicitly.
  • Hungarian tax specifics. Tax numbers, EU VAT, reverse charge (AAM), and NAV reporting rules are easy to get subtly wrong. Validate partner tax numbers before invoicing; a bad tax number produces a document that's painful to correct.
  • Don't invent invoice numbers. Billingo assigns them from your block. Generate a local draft reference, not the legal number.
  • Handle structured errors. The API returns machine-readable error bodies. Map 4xx to validation exceptions you can surface to users; retry 5xx with exponential backoff and a cap. Treat network timeouts as "unknown state" — re-query by your idempotency key rather than blindly retrying a create.
  • Log requests and responses, redact the key. You will need this the first time a payload is rejected with a vague message. Laravel's HTTP client logging or a middleware on the injected client both work.
  • Project risk is real. One star and near-zero dependents means the bus factor is you. Pin an exact version, read the source before relying on it, and be prepared to fork. Confirm the release tag actually corresponds to the code you're installing.
  • Extension points are likely the HTTP client and serializer, passed as constructor arguments. Override those bindings first if you need custom retry, logging, or auth refresh — before you consider forking.
  • Queue failures need a dead-letter path. A failed invoice job that vanishes is an unbilled customer. Alert on exhausted retries.
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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
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
cgv/localized-resource