andreaballarin / acube-italy-receipts
Acube Italy Receipts Library
Package info
github.com/andreaballarin/acube-italy-receipts
pkg:composer/andreaballarin/acube-italy-receipts
Requires
- php: ^8.3
- guzzlehttp/guzzle: ^7.8
- guzzlehttp/psr7: ^2.6
- psr/http-client: ^1.0
- psr/http-factory: ^1.0
- psr/http-message: ^1.1 || ^2.0
- psr/log: ^3.0
Requires (Dev)
- phpunit/phpunit: ^10.5
README
SDK PHP 8.3+ per l'integrazione delle API Acube per gli Scontrini Elettronici (Documento Commerciale italiano).
Installazione
composer require andreaballarin/acube-italy-receipts
Requisiti
- PHP: ^8.3
- PSR-18 HTTP Client: Guzzle ^7.8 (incluso)
- Certificati mTLS: Rilasciati da Acube per il tuo cash register
Quick Start
1. Login e ottieni il token JWT
use AndreaBallarin\ACubeItalyReceipts\{AuthenticationService, Environment}; $auth = new AuthenticationService(environment: Environment::Sandbox); $token = $auth->login( email: 'your@email.com', password: 'your-password', );
2. Setup del Client
use AndreaBallarin\ACubeItalyReceipts\Client; $client = new Client( bearerToken: $token, environment: Environment::Sandbox, mtlsCertPath: '/path/to/cert.pem', mtlsKeyPath: '/path/to/key.pem', mtlsKeyPassphrase: 'passphrase-if-any', // opzionale );
3. Registra il Client per l'utilizzo statico
use AndreaBallarin\ACubeItalyReceipts\Resources\Receipt; Receipt::setClient($client);
4. Crea uno scontrino
use AndreaBallarin\ACubeItalyReceipts\Resources\{ReceiptDraft, ReceiptItem}; $item = new ReceiptItem( quantity: '1.00', description: 'Espresso', unitPrice: '1.20', vatRateCode: '10.00', ); $draft = new ReceiptDraft( items: [$item], cashPaymentAmount: '1.20', ); $receipt = Receipt::create($draft); echo "UUID: " . $receipt->uuid; echo "Documento: " . $receipt->documentNumber;
5. Recupera e annulla
$fetched = Receipt::get($receipt->uuid); $voided = $fetched->void('Customer returned');
Onboarding: Merchant → PEM → Cash Register
Prima di poter emettere scontrini serve completare l'onboarding: creare un merchant, creare e attivare un PEM, poi creare il cash register che rilascia il certificato mTLS. Vedi examples/complete-supplier-onboarding.php per il flusso completo.
Importante: Merchant/Pem::create()/Pem::get()/Pem::activate()/CashRegister::create() usano la porta standard (nessun mTLS) — solo gli endpoint /mf1/receipts* (Receipt, ReceiptDetails) richiedono mTLS e vengono chiamati automaticamente sulla porta 444 quando il Client ha mtlsCertPath configurato. Usa quindi Client separati: uno senza mTLS per l'onboarding, uno con mTLS per gli scontrini. Poiché ApiResource::setClient() condivide un unico slot statico tra tutte le resource, richiama setClient() ogni volta che cambi Client.
1. Crea un merchant
use AndreaBallarin\ACubeItalyReceipts\Resources\{Merchant, MerchantDraft}; use AndreaBallarin\ACubeItalyReceipts\ValueObjects\Address; Merchant::setClient($supplierClient); // Client con il token del Supplier, senza mTLS $merchant = Merchant::create(new MerchantDraft( vatNumber: '12345678901', address: new Address( streetAddress: 'street name', streetNumber: 'xx', zipCode: '00000', city: 'City', province: 'XX', ), businessName: 'business name', email: 'merchant@example.com', password: 'MerchantP4$$w0rd', ));
2. Crea e attiva un PEM
use AndreaBallarin\ACubeItalyReceipts\Resources\Pem; $pem = Pem::create($merchant->uuid); // ancora col client Supplier Pem::setClient($merchantClient); // Client col token del Merchant, senza mTLS Pem::activate($pem->serialNumber, $pem->registrationKey);
3. Crea il cash register (rilascia il certificato mTLS)
use AndreaBallarin\ACubeItalyReceipts\Resources\CashRegister; CashRegister::setClient($merchantClient); $cashRegister = CashRegister::create($pem->serialNumber, 'Cash register first floor'); // mtlsCertificate e privateKey sono già "unescaped" (newline reali), pronti per il disco file_put_contents('/var/certs/pems/A2F4-000001/cr-cert.pem', $cashRegister->mtlsCertificate); file_put_contents('/var/certs/pems/A2F4-000001/cr-key.pem', $cashRegister->privateKey);
4. Recupera il dettaglio fiscale completo di uno scontrino
use AndreaBallarin\ACubeItalyReceipts\Resources\ReceiptDetails; $details = ReceiptDetails::get($receipt->uuid); // richiede lo stesso Client mTLS di Receipt echo "Imponibile: {$details->totalTaxableAmount}, IVA: {$details->totalVatAmount}"; foreach ($details->items as $item) { echo "{$item->description}: {$item->unitPrice}\n"; }
Login & Autenticazione
L'SDK include AuthenticationService per gestire il login presso Acube:
use AndreaBallarin\ACubeItalyReceipts\AuthenticationService; $auth = new AuthenticationService(environment: Environment::Sandbox); try { $token = $auth->login(email: 'user@example.com', password: 'secret'); // Usa il token con il Client } catch (AcubeAuthenticationException $e) { echo "Login failed: invalid credentials"; }
Note:
AuthenticationServicechiamaPOST https://common-sandbox.api.acubeapi.com/login(endpoint separato)- Non richiede mTLS (diversamente da eReceipts)
- Ritorna direttamente il JWT Bearer token
- Il token è necessario per creare il
Clientche accede agli scontrini
Features
Punto 1: Dependency Injection
Due modalità di utilizzo:
- Statica (Service Locator):
Receipt::setClient($client)+Receipt::create(...) - Instance-based (DI-friendly): Passa il client al costruttore se la tua classe lo supporta
Punto 2: DecimalAmount Validation
Tutti gli importi/quantità sono string per preservare la precisione. DecimalAmount::assertValid() fallisce velocemente:
new ReceiptItem( quantity: '1.00', // validato automaticamente unitPrice: '10.50', // validato );
Punto 3: UUID Validation
UuidValidator valida localmente prima di fare richieste HTTP:
Receipt::get('invalid-uuid'); // InvalidArgumentException locale, non 404 da Acube
Punto 4: PSR-3 Logging
Inietta un logger PSR-3 per registrare:
- Richieste POST critiche (creazione documenti)
- Errori API (status ≥400)
- Errori di trasporto
$client = new Client( bearerToken: $token, logger: $myLogger, // Psr\Log\LoggerInterface );
Gestione Errori
use AndreaBallarin\ACubeItalyReceipts\Exceptions\{ AcubeValidationException, AcubeNotFoundException, AcubeException, }; try { $receipt = Receipt::create($draft); } catch (AcubeValidationException $e) { foreach ($e->violations() as $field => $msg) { echo "$field: $msg"; } } catch (AcubeNotFoundException $e) { echo "Document not found"; } catch (AcubeException $e) { echo "API Error [{$e->problem->status}]: {$e->problem->detail}"; }
Idempotenza (Best-Effort, Client-Side)
$receipt = Receipt::create( draft: $draft, idempotencyKey: 'unique-key-123', idempotencyWindowSeconds: 60, ); // Se ripeti la stessa chiave entro 60 secondi: AcubeDuplicateRequestException
Nota: Questa guardia protegge solo dentro lo stesso processo. Acube non offre idempotency key server-side, quindi un timeout di rete potrebbe causare duplicati.
Test
composer test
Struttura
src/
Client.php # Transport PSR-18, logging PSR-3
Environment.php # Enum Sandbox|Production
Model.php # Base per i dati
ApiResource.php # Base per le risorse (setClient/client)
Internal/
DecimalAmount.php # Validazione importi
UuidValidator.php # Validazione UUID RFC 4122
IdempotencyGuard.php # Guardia client-side idempotenza
Exceptions/
AcubeException.php # Base astratta
AcubeAuthenticationException.php # 401
AcubeValidationException.php # 422
... (e altre per 403, 404, 409, 500, 503)
ValueObjects/
ProblemDetails.php # RFC 7807
Address.php # Indirizzo (merchant/PEM)
Resources/
Merchant.php # Merchant registrato (MerchantOutput)
MerchantDraft.php # Payload di creazione (MerchantInput)
Pem.php # PEM: create/get/activate
CashRegister.php # Cash register: create (rilascia mTLS)
Receipt.php # Documento emesso (ReceiptOutput)
ReceiptDraft.php # Payload di creazione (ReceiptInput)
ReceiptItem.php # Riga di dettaglio
ReceiptDetails.php # Dettaglio fiscale completo (righe + totali)
Enums/
ReceiptType.php
ReceiptStatus.php
ReceiptItemType.php
Licenza
MIT
Autore
Andrea Ballarin ballarinandrea@icloud.com