aichadigital / larabill
Professional billing & invoicing package for Laravel with UUID v7, VAT verification, tax calculation for Spain/EU/worldwide, and EU compliance
Fund package maintenance!
Requires
- php: ^8.3
- aichadigital/lara-privacy-core: ^1.0
- aichadigital/lara-verifactu: ^1.0
- aichadigital/lara100: ^2.0
- aichadigital/lararoi: ^0.5
- dompdf/dompdf: ^3.1
- illuminate/contracts: ^12.0||^13.0
- illuminate/database: ^12.0||^13.0
- illuminate/http: ^12.0||^13.0
- lorisleiva/laravel-actions: ^2.0
- spatie/laravel-package-tools: ^1.17
- spatie/laravel-translatable: ^6.12
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.25
- nunomaduro/collision: ^8.8
- nunomaduro/phpinsights: ^2.13
- orchestra/testbench: ^10.6||^11.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
This package is auto-updated.
Last update: 2026-06-29 18:26:22 UTC
README
βΉοΈ Schema upgrade policy β Larabill does not promise in-place schema upgrades between versions. Install fresh and seed with
larabill:install(ormigrate:fresh) rather than migrating an existing schema across major versions. See ADR-006.
Larabill is a professional, UUID-first billing and invoicing package for Laravel applications. It provides comprehensive VAT verification, tax calculation for Spain/EU/worldwide, and flexible invoice generation with immutability protection. The consumer app's users.id MUST be UUID v7 char(36) β see docs/setup-uuid.md and ADR-006.
π― Features
Core Functionality
- Invoice Management: UUID-based IDs, sequential numbering, proforma invoices, immutable records
- Tax Calculation: Spanish (IVA), Canary Islands (IGIC), Ceuta/Melilla (IPSI), EU reverse charge, worldwide
- VAT/Tax Code Verification: Integration with AbstractAPI and APILayer for real-time validation
- Fiscal Data Management: Company and customer fiscal configurations with temporal validity
- PDF Generation: Built-in invoice PDF generation using DomPDF
- EU Compliance: Full support for EU B2B reverse charge and destination VAT rules
Technical Excellence
- String UUID v7: Ordered UUIDs for invoices and the consumer's
users.id(ADR-006) - FixedDecimal money: Precise monetary value objects backed by base-100 integers (no floating-point errors)
- Preflight check:
larabill:installaborts cleanly whenusers.idis not UUID-compatible - Temporal Validity: Fiscal configurations with
valid_from/valid_untildates - Invoice Immutability: Protection against modifications after issuance
π¦ Requirements
- PHP ^8.3
- Laravel ^12.0 | ^13.0
users.idUUID v7 char(36) β seedocs/setup-uuid.md
π Installation
Via Composer
composer require aichadigital/larabill
Publish Configuration
php artisan vendor:publish --tag="larabill-config"
Run the Installer
php artisan larabill:install
This will:
- Publish migrations
- Run database migrations
- Seed default tax categories and rates
Manual Installation (if preferred)
# Publish migrations php artisan vendor:publish --tag="larabill-migrations" # Run migrations php artisan migrate # Seed default data php artisan db:seed --class="AichaDigital\Larabill\Database\Seeders\TaxCategoriesSeeder" php artisan db:seed --class="AichaDigital\Larabill\Database\Seeders\TaxRatesSeeder"
βοΈ Configuration
Environment Variables
Add these to your .env file:
# Tax Code Verification APIs LARABILL_ABSTRACTAPI_KEY="your_abstractapi_key" LARABILL_APILAYER_KEY="your_apilayer_key" LARABILL_VAT_PREFERRED_API="abstractapi" LARABILL_VAT_CACHE_DAYS=30 # Invoice Numbering LARABILL_INVOICE_PREFIX="FAC" LARABILL_PROFORMA_PREFIX="PRO" # Optional: override the User model class. Must use UUID v7 char(36) ids. LARABILL_USER_MODEL="App\\Models\\User"
Model Configuration
Configure your user model in config/larabill.php:
'models' => [ 'user' => \App\Models\User::class, 'invoice' => \AichaDigital\Larabill\Models\Invoice::class, 'invoice_item' => \AichaDigital\Larabill\Models\InvoiceItem::class, // ... ],
ποΈ Architecture
Fiscal Data Model
Larabill separates company and customer fiscal data with temporal validity:
CompanyFiscalConfig β Issuer fiscal settings (one active at a time)
UserTaxProfile β Customer fiscal data, temporally versioned per user
Invoice β Immutable invoice with fiscal snapshot
Key principles:
- The customer is a
User(ADR-003); businesses and sub-accounts are modelled withparent_user_id. The legacyCustomerFiscalDatamodel was removed. - Company config changes apply from a specific date forward
UserTaxProfilerecords are temporally versioned (valid_from/valid_until) β never modify past records- Invoices capture a fiscal snapshot at creation time
- Invoices are absolutely immutable once issued
UUID Strategy
Larabill uses string UUID v7 for invoices:
// Model with UUID use AichaDigital\Larabill\Concerns\HasUuid; class Invoice extends Model { use HasUuid; } // Migration $table->uuid('id')->primary();
Monetary Values (FixedDecimal)
Money is stored as base-100 integers and exposed as FixedDecimal value objects (from lara100), so there are no floating-point errors. You assign the unscaled base-100 integer; reading the attribute returns a FixedDecimal:
// Assign the base-100 integer (β¬12.34 β 1234): $invoice->total_amount = 1234; // Reading the attribute returns a FixedDecimal value object // (base-100 backed, scale 2) β not a raw int: $money = $invoice->total_amount; // FixedDecimal
Invoice and invoice-item money attributes use the FixedDecimalCast (scale 2) from the lara100 package. Note: query-builder access (->value(), ->sum(), ->where()) returns the raw integer, while Eloquent attribute access returns a FixedDecimal.
π Usage
Creating an Invoice
use AichaDigital\Larabill\Services\BillingService; $billingService = app(BillingService::class); $invoice = $billingService->createInvoice([ 'user_id' => $user->id, // UUID v7 of the customer (a User; ADR-003) 'items' => [ [ 'description' => 'Professional Service', 'quantity' => 100, // base-100: 100 = 1.0 unit 'unit_price' => 10000, // base-100: 10000 = β¬100.00 'tax_group_id' => $taxGroup->id, // resolves the applicable VAT/IGIC/IPSI ], ], ]);
Tax Calculation
use AichaDigital\Larabill\Services\TaxCalculationService; $taxService = app(TaxCalculationService::class); // Calculate taxes for a single line. Amounts are base-100 integers. The // applicable rate (Spanish IVA, Canary IGIC, Ceuta/Melilla IPSI, EU reverse // charge or destination VAT) is resolved from the TaxGroup and the customer's // fiscal profile β not passed in directly. $result = $taxService->calculateForInvoiceItem([ 'quantity' => 100, // base-100: 1.0 unit 'base_price' => 10000, // base-100: β¬100.00 'tax_group_id' => $taxGroup->id, 'billable_user_id' => $user->id, // optional: drives B2B / destination rules ]); // $result keys (base-100 integers + breakdown): // taxable_amount, total_tax_amount, total_amount, tax_group_id, taxes_applied
VAT Verification
use AichaDigital\Larabill\Services\VatVerificationService; $vatService = app(VatVerificationService::class); $result = $vatService->verifyVatNumber('ESB12345678', 'ES'); if ($result['is_valid']) { echo "Valid VAT for: " . $result['company_name']; }
Company Fiscal Configuration
use AichaDigital\Larabill\Models\CompanyFiscalConfig; // Get current active config $config = CompanyFiscalConfig::getActive(); // Create new config (the previous active one is auto-closed) $newConfig = CompanyFiscalConfig::createNew([ 'tax_id' => 'ESB12345678', 'business_name' => 'Your Company S.L.', 'address' => 'Calle Test 123', 'city' => 'Madrid', 'zip_code' => '28001', 'country_code' => 'ES', 'is_oss' => true, 'valid_from' => now(), ]);
User Tax Profile
The customer is a User (ADR-003); their fiscal data lives in UserTaxProfile, temporally versioned. This replaces the removed CustomerFiscalData model.
use AichaDigital\Larabill\Models\UserTaxProfile; // Get the active fiscal profile for a user $profile = UserTaxProfile::getActiveForOwner($user->id); // Create a new profile (previous stays as history) $newProfile = UserTaxProfile::createForOwner($user->id, [ 'fiscal_name' => 'Client SARL', 'tax_id' => 'FR12345678901', 'country_code' => 'FR', 'is_company' => true, ]);
π§ͺ Testing
# Run all tests composer test # Run specific tests composer test -- --filter=Invoice # Run with coverage composer test-coverage # Static analysis vendor/bin/phpstan analyse
Current status (v3.1.1): 1021 tests passing on SQLite, plus MySQL 8 integration tests (real column types and unique constraints) and fork-based concurrency tests. The UUID-first contract is demonstrated on MySQL 8.
π Documentation
| Document | Description |
|---|---|
| ARCHITECTURE.md | Core architecture and domain model |
| setup-uuid.md | UUID-first onboarding for the consumer app |
| ADR-006 | UUID-first decision (supersedes the agnostic id contract) |
| TAX_RATES_MIGRATION_GUIDE.md | Tax rates migration guide |
| CHANGELOG.md | Version history and breaking changes |
For AI agents working with this package, see .claude/project.md.
πΊοΈ Roadmap
Shipped
- β Core invoice management (immutable records, UUID v7, sequential numbering, proforma)
- β Spanish tax system (IVA, IGIC, IPSI)
- β EU reverse charge (B2B) and destination VAT
- β
Fiscal data with temporal validity (
CompanyFiscalConfig,UserTaxProfile) - β
FixedDecimalmoney type (base-100, no floating-point errors) - β
VeriFACTU integration (Spain AEAT) via
lara-verifactu - β Grouped payments
- β
Legal-retention contract (
LegallyRetainable) for GDPR tooling
Under consideration
- Subscription billing
- Payment gateway integration (Stripe, PayPal, Redsys)
- Advanced reporting
See the CHANGELOG for the full release history.
π€ Contributing
Please see CONTRIBUTING for details.
π Security
Please review our security policy on how to report security vulnerabilities.
π License
GNU Affero General Public License v3.0 (AGPL-3.0-or-later). See LICENSE.md for details.
This means:
- β You can use, modify, and distribute this software
- β You must share any modifications under the same license
- β οΈ If you run this as a network service, you must provide the source code to users
- β οΈ You must preserve copyright and attribution notices