Installation
composer require langleyfoxall/xero-laravel
php artisan vendor:publish --provider="LangleyFoxall\XeroLaravel\Providers\XeroLaravelServiceProvider"
Configure config/xero-laravel-lf.php with your Xero OAuth credentials (client ID, secret, redirect URI).
Set Up OAuth
Register a callback route in routes/web.php:
Route::get('/xero/callback', [XeroController::class, 'handleCallback']);
Add the provider to config/auth.php under providers:
'xero' => [
'driver' => 'xero',
'model' => LangleyFoxall\XeroLaravel\Models\User::class,
],
First Query Use the Eloquent-like syntax to fetch contacts:
use LangleyFoxall\XeroLaravel\Facades\Xero;
$contacts = Xero::contacts()->get();
Or via a model:
$contact = XeroContact::find(123);
Authentication Flow Redirect users to Xero for OAuth:
return Xero::auth()->redirect();
CRUD Operations Use Eloquent-like methods for Xero entities (Contacts, Invoices, etc.):
// Create
$contact = Xero::contacts()->create([
'FirstName' => 'John',
'LastName' => 'Doe',
'EmailAddress' => 'john@example.com'
]);
// Read
$contact = XeroContact::find(123);
// Update
$contact->update(['EmailAddress' => 'new@example.com']);
// Delete
$contact->delete();
Relationships
Fetch related entities using with():
$invoice = XeroInvoice::with('contact')->find(456);
Pagination
$contacts = Xero::contacts()->paginate(10);
Custom Endpoints
Access non-standard Xero endpoints via Xero::api():
$reports = Xero::api('Reports')->get();
Middleware for Auth Protect routes requiring Xero access:
Route::middleware(['auth:xero'])->group(function () {
// Xero-specific routes
});
Event Listeners Listen for Xero webhook events (e.g., invoice updates):
Xero::listen('invoice.created', function ($invoice) {
// Handle new invoice
});
Batch Operations
Use chunk() for large datasets:
Xero::contacts()->chunk(100, function ($contacts) {
foreach ($contacts as $contact) {
// Process contact
}
});
Local Testing
Use the Xero::setTestMode(true) to test against Xero’s sandbox environment.
OAuth Scopes
Ensure your Xero app has the correct OAuth scopes (e.g., accounting.transactions.read). Missing scopes will cause 403 Forbidden errors.
Rate Limiting Xero enforces rate limits (e.g., 60 requests/minute). Cache responses aggressively:
$contacts = Cache::remember('xero_contacts', now()->addHours(1), function () {
return Xero::contacts()->get();
});
Idempotency
Xero APIs are not always idempotent. Use PUT for updates and handle conflicts:
try {
$contact->update([...]);
} catch (\LangleyFoxall\XeroLaravel\Exceptions\ConflictException $e) {
// Handle conflict (e.g., retry or notify admin)
}
Model Caching Disable caching for models if data changes frequently:
class XeroContact extends \LangleyFoxall\XeroLaravel\Models\Contact
{
public $cacheFor = null; // Disable caching
}
Enable Logging
Configure logging in config/xero-laravel-lf.php:
'logging' => [
'enabled' => true,
'channel' => 'single',
],
Check logs for OAuth tokens, API responses, and errors.
Token Refresh
If tokens expire, implement a refreshToken middleware or listener:
Xero::auth()->refreshToken();
API Errors Handle Xero-specific exceptions:
try {
$invoice = XeroInvoice::find(123);
} catch (\LangleyFoxall\XeroLaravel\Exceptions\NotFoundException $e) {
// Invoice not found
}
Custom Models Extend base models to add business logic:
class CustomInvoice extends \LangleyFoxall\XeroLaravel\Models\Invoice
{
public function customMethod() {
// Add custom logic
}
}
API Extensions Add support for custom Xero endpoints:
Xero::extend('custom_endpoint', function ($api) {
return $api->get('/api.xro/2.0/CustomEndpoint');
});
Webhook Handling Register custom webhook handlers:
Xero::listen('*.created', function ($payload) {
// Handle all "created" events
});
Testing
Use the XeroFake facade for unit tests:
XeroFake::fake([
'contacts' => [['id' => 1, 'FirstName' => 'Test']],
]);
How can I help you explore Laravel packages today?