ebethus / laravel-ticketbai
TicketBai Library for Laravel
Requires
- php: ^8.2
- barnetik/ticketbai: dev-main
- simplesoftwareio/simple-qrcode: 4.2
Requires (Dev)
- larastan/larastan: 2.x-dev
- laravel/pint: ^1.13
- orchestra/testbench: ^7.0|^8.0|^9.0|^10.0
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^9.5|^10.0
This package is auto-updated.
Last update: 2026-03-06 15:02:58 UTC
README
A Laravel package for generating and submitting TicketBAI (Ticket BAI) invoices for the Basque Country (Euskadi), Spain. This package provides a flexible and configurable solution for integrating TicketBAI compliance into your Laravel application.
Table of Contents
- Features
- Requirements
- Installation
- Configuration
- Usage
- Database Configuration
- API Reference
- Examples
- Testing
- Contributing
- License
Features
- ✅ Generate TicketBAI-compliant invoices
- ✅ Automatic invoice signing with X.509 certificates
- ✅ Queue-based invoice submission to TicketBAI API
- ✅ Flexible database table and column configuration
- ✅ Support for custom table structures
- ✅ Optional columns (signature, data, territory) for maximum flexibility
- ✅ Encadenamiento: firma y territorio siempre en columna JSON
databajo clave configurable (por defectoticketbai) - ✅ QR code generation for invoices
- ✅ Support for multiple territories (Araba, Bizkaia, Gipuzkoa)
- ✅ Automatic fingerprint calculation from previous invoices
- ✅ Artisan command to resend failed/pending invoices (
ticketbai:resend)
Requirements
- PHP >= 8.2
- Laravel >= 8.0 (tested with Laravel 10.x and 11.x)
- barnetik/ticketbai package
- X.509 certificate (.p12 file) for signing invoices
- TicketBAI license and credentials
Installation
Install the package via Composer:
composer require ebethus/laravel-ticketbai
Publish Configuration
Publish the configuration file:
php artisan vendor:publish --tag=ticketbai-config
This will create config/ticketbai.php where you can configure table and column mappings.
Run Migrations
If you want to use the default invoice table structure:
php artisan migrate
Note: You can also use your own table structure by configuring column mappings (see Database Configuration).
Configuration
Service Configuration
Add your TicketBAI credentials to config/services.php:
'ticketbai' => [ 'license' => env('TICKETBAI_LICENSE', ''), 'nif' => env('TICKETBAI_NIF', ''), 'appName' => env('TICKETBAI_APP_NAME', ''), 'appVersion' => env('TICKETBAI_APP_VERSION', ''), 'certPassword' => env('TICKETBAI_CERT_PASSWORD', ''), 'disk' => env('TICKETBAI_DISK', 'local'), ],
Environment Variables
Add these to your .env file:
TICKETBAI_LICENSE=TB12345678 TICKETBAI_NIF=B1111111 TICKETBAI_APP_NAME=My Application TICKETBAI_APP_VERSION=1.0 TICKETBAI_CERT_PASSWORD=your_certificate_password TICKETBAI_DISK=local TICKETBAI_CERT_PATH=certificado.p12
Use TICKETBAI_CERT_PATH to override the certificate path. It can be a path relative to storage_path() (e.g. certificado.p12 for storage/certificado.p12) or an absolute path (e.g. /etc/certs/ticketbai.p12 on Linux).
Certificate Setup
By default the package looks for the X.509 certificate (.p12) at storage/certificado.p12. Set TICKETBAI_CERT_PATH in your .env to use a different path (relative to storage_path() or absolute). The certificate is used to sign invoices before submission.
Usage
Basic Example
use EBethus\LaravelTicketBAI\TicketBAI; // Get TicketBAI instance (configured via service provider) $ticketbai = app('ticketbai'); // Or use the facade use TicketBAI; // Set issuer information $ticketbai->issuer( nif: 'B12345678', name: 'Company Name', idIssuer: 1, serie: '' // Optional ); // Set VAT percentage $ticketbai->setVat(21); // 21% VAT // Add invoice items $ticketbai->add( desc: 'Product description', unitPrice: 100.00, q: 2, discount: 0 // Optional ); // Generate and sign invoice $qrUrl = $ticketbai->invoice( territory: 'BIZKAIA', // or 'ARABA', 'GIPUZKOA' description: 'Invoice description' ); // The invoice is automatically saved and queued for submission // $qrUrl contains the QR code URL for the invoice
Using Dependency Injection
use EBethus\LaravelTicketBAI\TicketBAI; class InvoiceController extends Controller { public function __construct( protected TicketBAI $ticketbai ) {} public function create(Request $request) { $this->ticketbai->issuer( nif: $request->nif, name: $request->company_name, idIssuer: $request->issuer_id ); $this->ticketbai->setVat(21); foreach ($request->items as $item) { $this->ticketbai->add( desc: $item['description'], unitPrice: $item['price'], q: $item['quantity'], discount: $item['discount'] ?? 0 ); } $qrUrl = $this->ticketbai->invoice( territory: 'BIZKAIA', description: $request->description ); return response()->json(['qr_url' => $qrUrl]); } }
Adding Extra Data
You can attach additional JSON data to invoices:
$ticketbai->data([ 'order_id' => 12345, 'customer_id' => 67890, 'custom_field' => 'value' ]);
This data will be stored in the data column if configured (see Database Configuration).
Database Configuration
The library supports flexible table and column configuration, allowing you to use your existing database structure.
Default Table Structure
The default migration creates an invoices table with columns issuer, provider_reference, path, data, sent, and timestamps. TicketBAI stores signature and territory in the data JSON column under the key ticketbai.
Custom Table Configuration
If you use your own table with different column names (e.g. transaction_id instead of issuer), override the mappings via environment variables. Example: TICKETBAI_COLUMN_ISSUER=transaction_id, TICKETBAI_COLUMN_NUMBER=invoice_number, TICKETBAI_COLUMN_SENT=attempted_at.
Configure column mappings in config/ticketbai.php:
'table' => [ 'name' => env('TICKETBAI_TABLE_NAME', 'invoices'), 'columns' => [ // Defaults match the default migration. For your own table use env, e.g.: // TICKETBAI_COLUMN_ISSUER=transaction_id, TICKETBAI_COLUMN_NUMBER=invoice_number 'issuer' => env('TICKETBAI_COLUMN_ISSUER', 'issuer'), 'number' => env('TICKETBAI_COLUMN_NUMBER', 'provider_reference'), 'territory' => env('TICKETBAI_COLUMN_TERRITORY', 'territory'), 'signature' => env('TICKETBAI_COLUMN_SIGNATURE', 'signature'), 'path' => env('TICKETBAI_COLUMN_PATH', 'path'), 'data' => env('TICKETBAI_COLUMN_DATA', 'data'), 'sent' => env('TICKETBAI_COLUMN_SENT', 'sent'), 'created_at' => env('TICKETBAI_COLUMN_CREATED_AT', 'created_at'), 'updated_at' => env('TICKETBAI_COLUMN_UPDATED_AT', 'updated_at'), ], ],
Environment Variables for Column Mapping
Use these only when you have a custom table with different column names:
TICKETBAI_TABLE_NAME=invoices TICKETBAI_COLUMN_ISSUER=transaction_id TICKETBAI_COLUMN_NUMBER=provider_reference TICKETBAI_COLUMN_TERRITORY=territory TICKETBAI_COLUMN_SIGNATURE=signature TICKETBAI_COLUMN_PATH=path TICKETBAI_COLUMN_DATA=data TICKETBAI_COLUMN_SENT=attempted_at TICKETBAI_COLUMN_CREATED_AT=created_at TICKETBAI_COLUMN_UPDATED_AT=updated_at
TicketBAI payload in data (required for chaining)
Signature and territory are always stored in the JSON data column under a configurable key so that encadenamiento (signature chaining) works. Default key: ticketbai. Set in .env or config:
TICKETBAI_DATA_KEY=ticketbai
The package stores and reads signature (first 100 chars) and territory under data->ticketbai. Path stays in the path column. Example:
{
"ticketbai": {
"signature": "first 100 chars of chain signature",
"territory": "02"
},
"order_id": 12345
}
Your table needs: id, issuer, number, path (file path), data (JSON), sent, created_at, updated_at. Other providers can use other keys in data (e.g. data->other_provider).
Optional column name override
data: Set tonullor empty to use the default column name'data'.
Required Columns
path: Required - stores the signed XML file path (filesystem).data: Required - JSON column where TicketBAI stores signature and territory underTICKETBAI_DATA_KEY.
API Reference
TicketBAI Class
issuer(string $nif, string $name, int $idIssuer, string $serie = '')
Set the issuer information for the invoice.
$nif: Tax identification number (NIF/CIF)$name: Company name$idIssuer: Internal issuer ID (used for database storage)$serie: Optional invoice series
setVat(float $vatPerc)
Set the VAT percentage for invoice items.
$vatPerc: VAT percentage (e.g., 21 for 21%)
add(string $desc, float $unitPrice, float $q, float $discount = null)
Add an item to the invoice.
$desc: Item description$unitPrice: Unit price (including VAT)$q: Quantity$discount: Optional discount amount
data(mixed $data)
Attach additional JSON data to the invoice.
$data: Array or object to be stored as JSON
invoice(string $territory, string $description)
Generate, sign, and save the invoice. Returns the QR code URL.
$territory: Territory code:'ARABA','BIZKAIA', or'GIPUZKOA'(or numeric codes'01','02','03')$description: Invoice description
Returns: string - QR code URL
getModel()
Get the Eloquent model instance for the saved invoice.
Returns: Invoice
getTBAI()
Get the underlying TicketBAI object from the barnetik/ticketbai package.
Returns: \Barnetik\Tbai\TicketBai
Examples
Complete Invoice Example
use EBethus\LaravelTicketBAI\TicketBAI; $ticketbai = app('ticketbai'); // Configure issuer $ticketbai->issuer( nif: 'B12345678', name: 'My Company S.L.', idIssuer: 1 ); // Set VAT $ticketbai->setVat(21); // Add items $ticketbai->add('Product A', 50.00, 2, 0); $ticketbai->add('Product B', 30.00, 1, 5.00); // Add extra data $ticketbai->data([ 'order_id' => 12345, 'customer_email' => 'customer@example.com' ]); // Generate invoice $qrUrl = $ticketbai->invoice( territory: 'BIZKAIA', description: 'Order #12345' ); echo "QR Code: $qrUrl";
Using with Custom Table
// In config/ticketbai.php or .env // TICKETBAI_TABLE_NAME=my_invoices // TICKETBAI_COLUMN_ISSUER=user_id // TICKETBAI_COLUMN_NUMBER=invoice_ref // TICKETBAI_COLUMN_SIGNATURE= (empty, disabled) // TICKETBAI_COLUMN_DATA= (empty, disabled) $ticketbai = app('ticketbai'); $ticketbai->issuer('B12345678', 'Company', 1); $ticketbai->setVat(21); $ticketbai->add('Item', 100, 1); $qrUrl = $ticketbai->invoice('BIZKAIA', 'Invoice');
Accessing Saved Invoice
$ticketbai = app('ticketbai'); // ... configure and generate invoice ... $model = $ticketbai->getModel(); echo $model->provider_reference; // Invoice number (default column name) echo $model->path; // XML file path
Testing
Run the test suite:
composer test
Or with PHPUnit directly:
./vendor/bin/phpunit
Optional: run static analysis (PHPStan) and code style (Laravel Pint):
composer analyse # PHPStan composer format # Pint (fixes style)
Queue Configuration
Invoice submission is asynchronous: after generating and signing an invoice, the package dispatches an InvoiceSend job to the Laravel queue. You must have at least one queue worker running for invoices to be sent to the TicketBAI API:
php artisan queue:work
- Production: Use a process manager (e.g. Supervisor) to keep
queue:workrunning. - Testing / sync: If you use
QUEUE_CONNECTION=sync, jobs run immediately in the same process (no worker needed, but slower and no retries).
The InvoiceSend job submits the invoice to the TicketBAI API and updates the sent timestamp on success.
Resending Failed or Pending Invoices
Invoices that were not sent (e.g. API error or worker down) have sent = null. To re-queue them for sending:
# List and resend all pending invoices php artisan ticketbai:resend --all # Resend a single invoice by ID php artisan ticketbai:resend --id=123 # Dry run: only list what would be resent php artisan ticketbai:resend --all --dry-run
Resend requires territory and path: territory is read from data[ticketbai_data_key] (default data->ticketbai), path from the path column.
Troubleshooting
Certificate Errors
- Ensure
storage/certificado.p12exists and is readable - Verify the certificate password is correct
- Check file permissions
Database Column Errors
- Verify column mappings in
config/ticketbai.php - Ensure required columns exist in your table
- Set optional columns to
nullif not needed
Queue Issues
- Ensure queue worker is running
- Check queue connection configuration
- Review failed jobs:
php artisan queue:failed
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This package is open-sourced software licensed under the MIT license.
Credits
- Built on top of barnetik/ticketbai
- Developed by EBethus
Support
For issues and feature requests, please use the GitHub issue tracker.