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.
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.
composer require padam87/billingo-bundle (and note the underlying API client it pulls in — check composer.json)..env as BILLINGO_API_KEY; never commit it.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).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.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.
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.
%env(...)%) doesn't happen. Validate your own config explicitly and fail loudly at boot if the key is missing.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.YYYY-MM-DD. Don't pass Carbon objects or timestamps and hope the serializer handles it; format explicitly.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.How can I help you explore Laravel packages today?