verifacturapi / php-sdk
Official framework-agnostic PHP SDK for Verifacturapi - Spain's AEAT Verifactu e-invoicing API
Requires
- php: ^8.2
- ext-json: *
- guzzlehttp/guzzle: ^7.8
- psr/http-client: ^1.0
- psr/http-message: ^1.1 || ^2.0
Requires (Dev)
- guzzlehttp/psr7: ^2.6
- laravel/pint: ^1.16
- pestphp/pest: ^2.34
- phpstan/phpstan: ^1.11
README
Official, framework-agnostic PHP SDK for Verifacturapi — a wrapper around Spain's AEAT Verifactu e-invoicing system.
- PHP 8.2+
- Thin HTTP client over the
/api/v1REST API (does not re-implement AEAT) - Typed exceptions, multi-CIF, provisioning, webhook signature verification
- Pluggable transport (Guzzle by default) — trivial to fake in tests
Installation
composer require verifacturapi/php-sdk
Quick start
use Verifacturapi\Client;
$client = Client::make('your-api-token');
// Custom host: Client::make('token', 'https://api.verifacturapi.com')
// Health check
$client->system()->ping();
// Create an invoice (see API docs for the full payload)
$result = $client->invoices()->create([
'series' => 'F',
'number' => '2026-000123',
// ...issuer, lines, taxes...
]);
echo $result['uuid'], ' -> ', $result['status'];
Resources
| Accessor | Covers |
|---|---|
$client->invoices() | create, correct, cancel, rectify, list, find, status, listPresented, export |
$client->catalogs() | invoiceTypes, taxCodes, regimeCodes, operationQualifications, exemptions, validateNif |
$client->cifs() | multi-CIF: list, selector, create, find, update, setDefault, toggleActive, uploadCertificate, delete |
$client->webhooks() | list, create, find, update, delete, deliveries |
$client->analytics() | dashboard, monthlyComparison, successRate, responseTime, dailyTrend, statusDistribution |
$client->provisioning() | userExists, provisionUser (service token) |
$client->system() | ping, user, testConnection |
Every method returns the decoded JSON body as an associative array. invoices()->export() returns the raw Response (e.g. CSV).
Licensing (M2M) endpoints are intentionally out of scope: this SDK targets the user API token surface only.
Multi-CIF certificate upload
$client->cifs()->uploadCertificate($cifId, '/path/cert.p12', 'cert-password');
Provisioning (host applications)
Provisioning requires a service token (abilities users:read, users:write). It lets a host app create Verifacturapi accounts for its own users transparently.
$service = Client::make('service-master-token');
if (! ($service->provisioning()->userExists('user@host.com')['exists'] ?? false)) {
$tenant = $service->provisioning()->provisionUser('Acme SL', 'user@host.com', 'perfex_module');
$tenantToken = $tenant['token']; // store it; shown once
}
Webhooks: verify the signature
Deliveries are signed with HMAC-SHA256 over "{timestamp}.{rawJsonBody}" and travel in the headers X-Webhook-Signature and X-Webhook-Timestamp.
use Verifacturapi\Webhooks\SignatureVerifier;
$verifier = new SignatureVerifier($webhookSecret);
$raw = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$timestamp = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '';
if (! $verifier->verify($raw, $signature, $timestamp)) {
http_response_code(400);
exit;
}
$event = json_decode($raw, true);
Error handling
Non-2xx responses raise typed exceptions (all extend Verifacturapi\Exceptions\ApiException):
| Status | Exception |
|---|---|
| 401 | AuthenticationException |
| 404 | NotFoundException |
| 422 | ValidationException (->errors()) |
| 429 | RateLimitException (->retryAfter()) |
| other | ApiException (->status(), ->body()) |
Network failures raise TransportException.
use Verifacturapi\Exceptions\ValidationException;
try {
$client->invoices()->create([]);
} catch (ValidationException $e) {
foreach ($e->errors() as $field => $messages) {
// ...
}
}
Testing without network
Inject FakeTransport: queue responses, assert on the requests made.
use Verifacturapi\Client;
use Verifacturapi\Config;
use Verifacturapi\Http\FakeTransport;
$transport = (new FakeTransport)->pushJson(['uuid' => 'abc', 'status' => 'register']);
$client = new Client(new Config('token', 'https://api.test'), $transport);
$client->invoices()->create(['series' => 'F']);
$transport->lastRequest(); // ['method' => 'POST', 'uri' => 'verifactu/create', 'options' => [...]]
Live smoke test
A ready-to-run, read-only example lives in examples/smoke.php.
It reads the token from the environment — never hardcode credentials:
VERIFACTURAPI_TOKEN=your-token \
VERIFACTURAPI_BASE_URL=https://api.verifacturapi.com \
php examples/smoke.php
Configuration
Config lets you tune the host, API prefix, timeouts and extra headers:
use Verifacturapi\Client;
use Verifacturapi\Config;
$client = new Client(new Config(
token: 'your-token',
baseUrl: 'https://api.verifacturapi.com',
apiPrefix: '/api/v1',
timeout: 30.0,
));
Development
composer install
composer quality # pint (test) + phpstan + pest
License
MIT