fourlabs/xero-bundle
A PHP package that bundles Xero accounting integration for your application, offering configuration and service wiring to connect to the Xero API and work with invoices, contacts, and other accounting data with less boilerplate.
This bundle is thin — it wires calcinai/xero-php into the Symfony container and maps a config block onto the client constructor. There is very little code, so read the three files that constitute it before anything else:
src/DependencyInjection/Configuration.php — the exact config keys (consumer key/secret, RSA private key or cert path, callback URI) and their defaults.src/DependencyInjection/*Extension.php — how those keys are turned into service arguments, and the service alias you should inject.src/Resources/config/services.* — the service id you'll type-hint.Minimal path:
composer require fourlabs/xero-bundle:^1.2 calcinai/xero-php:^1.8
// config/bundles.php
FourLabs\XeroBundle\FourLabsXeroBundle::class => ['all' => true],
# config/packages/xero.yaml — confirm the root key in Configuration.php first
four_labs_xero:
consumer_key: '%env(XERO_KEY)%'
consumer_secret: '%env(XERO_SECRET)%'
private_key: '%kernel.project_dir%/var/xero/privatekey.pem'
First use case: prove auth before building anything. Inject the client into a console command and do one read — getConnections(), then a ->load('Accounting\\Contact')->execute() — so you learn immediately whether the auth flow works at all against today's Xero. Do not start with write paths.
1.2.0 changes nothing you can use. The release is a single dependency bump to calcinai/xero-php 1.8; the bundle's config keys, service ids, and constructor wiring are untouched. So there is no migration to do within the bundle — the entire question is what that 1.8 constraint resolves to. Before writing a line of integration code, run composer show calcinai/xero-php and composer why calcinai/xero-php. If the resolved tree still drags in the XeroPHP\Application\PrivateApplication / RSA-signature OAuth 1.0a path, the bump is cosmetic and the auth risk described below still stands.
If you're a Laravel developer: this is a Symfony bundle and will not boot under Laravel. It remains a reference implementation for how to configure calcinai/xero-php, and 1.2.0 doesn't change that. Register the client in your own XeroServiceProvider rather than pulling Symfony bundle machinery in for one class. The bump does make the ^1.8 constraint easier to satisfy alongside a modern Laravel's Guzzle, which is worth a composer update --dry-run to confirm.
xero-tenant-id) per connection in your DB; refresh proactively on expiry and reactively on 401.getConnections() returns them; every Accounting API call is tenant-scoped. Don't hold a mutable "current tenant" on a singleton client — resolve a tenant-scoped client per job/request.ContactID/InvoiceID, and use UpdatedDateUTC for delta pulls. Rate limits are ~60 calls/min and ~5,000/day per tenant, so a scheduled job with a rate limiter is the only workable pattern.executeWithErrors() and inspect the returned validation payload — a partially failed batch returns 200 with per-item failures.syncContacts, createInvoice). The bundle is unmaintained and the 1.8 bump is the last thing likely to happen to it; the wrapper is your escape hatch when you replace it.composer update calcinai/xero-php --with-dependencies, then run the auth smoke command from Getting Started against a sandbox tenant before merging. If it fails, the revert is a one-line constraint change — keep it that way.calcinai/xero-php 1.x is the OAuth 1.0a / RSA-signature line (PrivateApplication, PublicApplication, PartnerApplication); OAuth 2 support arrived in the 2.x line (with a separate OAuth2 provider package). Xero has retired OAuth 1.0a, so a ^1.8 constraint that resolves inside 1.x is very likely still unusable for new apps and for any app re-authorising today. Confirm the resolved major before committing to this package — this remains the single thing that kills it for most projects.psr/* constraints. Run composer update --dry-run first and read the full resolved diff; a Guzzle major bump in a Laravel app touches HTTP clients you didn't intend to change.getContainerExtension() doesn't always match the package name. If your YAML is silently ignored, that's why.bin/console cache:clear --env=prod. In prod the container is baked — config changes without a deploy do nothing.php-fpm user, and (if passphrase-protected) have the passphrase supplied separately. Permission errors surface as opaque auth failures — and under OAuth 1.0a you'll hit them constantly, since every request is signed.Calcinai\XeroPHP\Exception\* and dump the decoded error array — the human-readable message is almost always in ValidationErrors or Message, not getMessage().services.yaml under the same id rather than forking. That covers logging middleware, custom token stores, and timeouts.How can I help you explore Laravel packages today?