Skip to content

ITSWProj/ecr17-php-client

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Protocollo 17 POS — PHP Client / Client PHP

A lightweight, dependency-free PHP client for the Italian "Protocollo 17" / ECR17 amount-exchange protocol, used between a cash register (ECR) and a payment terminal (POS) over TCP/IP.

Client PHP leggero e senza dipendenze per il protocollo italiano "Protocollo 17" / ECR17 di scambio importo tra registratore di cassa (ECR) e terminale POS via TCP/IP.

🇬🇧 English below · 🇮🇹 Versione italiana più sotto


🇮🇹 Italiano

Perché esiste

Il "Protocollo 17" (noto anche come ECR17 o protocollo Ingenico) è lo standard di fatto in Italia per lo scambio importo tra cassa e POS. Con la normativa che impone il collegamento POS–registratore telematico, sempre più sviluppatori hanno bisogno di integrarlo — ma le specifiche non sono pubblicate dagli acquirer e vengono fornite solo sotto NDA agli integratori di gestionali. Il risultato è che le community di sviluppatori italiani si arrabattano da anni senza una documentazione aperta.

Questa libreria nasce per colmare quel vuoto: è il punto di partenza che avremmo voluto trovare noi. Il framing è stato ricostruito basandosi sulla documentazione pubblica Nexi "Traditional POS", liberamente accessibile, dello stesso standard ECR17.

📖 Fonte ufficiale del protocollo (pubblica): https://developer.nexigroup.com/traditionalpos/en-EU/docs/

Installazione

Non è un pacchetto Composer: copia semplicemente src/Protocol17Client.php nel tuo progetto e adatta il namespace alle tue esigenze. Richiede PHP 8.1+ (usa la sintassi dei parametri nominati e match).

Uso rapido

use Protocollo17\Pos\Protocol17Client;

$client = new Protocol17Client(
    host:           '192.168.1.100',  // IP del terminale POS
    port:           10000,            // porta TCP (spesso 10000)
    terminalId:     '00000000',       // TID del POS; '00000000' = jolly per test
    cashRegisterId: '00000001',       // ID cassa, a tua scelta
    lrcMode:        'stx',            // modalità LRC (vedi sotto)
);

// Importo SEMPRE in centesimi: 100 = 1,00 €
$result = $client->pay(100, function (string $status) {
    echo "Stato POS: $status\n"; // es. "Inserire carta", "Digitare PIN"
});

if ($result['approved']) {
    echo "Approvata! Autorizzazione: {$result['auth_code']}\n";
} else {
    echo "Negata: {$result['description']}\n";
}

Il parametro lrcMode (importante!)

L'LRC (carattere di controllo) si calcola in XOR partendo da un valore base 0x7F (non 0x00 come negli LRC standard — questo è il dettaglio che blocca quasi tutti). Quali byte entrano nel calcolo dipende però dal terminale/acquirer. Sono previste quattro modalità:

lrcMode Calcolo
stx 0x7F ^ STX ^ payload ^ ETX
std 0x7F ^ payload ^ ETX
noetx 0x7F ^ payload
stx_noetx 0x7F ^ STX ^ payload

Come trovare quella giusta: parti con stx. Se ricevi un NAK, prova le altre una per una con un importo minimo (1,00 €). Quando il display del POS si accende e chiede la carta, hai trovato la modalità corretta per il tuo terminale.

Struttura del risultato

In caso di transazione approvata (result === '00') l'array contiene tra l'altro: approved, pan, auth_code, read_type (ICC/MAG/CLM…), card_type, stan, datetime. In caso di transazione negata contiene description con il motivo. Il campo raw è sempre presente: contiene il payload grezzo, utilissimo per il debug e per verificare le posizioni dei campi sul TUO terminale.

Configurazione del POS

Fatevi confermare dall'installatore/acquirer e impostate sul terminale:

  • IP statico (DHCP disabilitato)
  • Protocollo 17 abilitato
  • IP cassa = l'IP della macchina dove gira questo codice
  • Porta TCP (spesso 10000)
  • Su alcuni PAX: voce "Conferma importo da ECR" impostata a NO

Note operative

  • Una transazione alla volta: il POS gestisce una sola sessione attiva.
  • Usate importi minimi nei test e gestite gli storni con cura.
  • Il readTimeout deve coprire l'intera transazione (attesa carta + PIN + host): 90–120s.
  • I messaggi di stato (onStatus) sono perfetti per aggiornare il display cassa in tempo reale.

🇬🇧 English

Why this exists

"Protocollo 17" (also known as ECR17 or the Ingenico protocol) is the de-facto Italian standard for amount exchange between a cash register and a POS terminal. As regulations increasingly require linking the POS to fiscal cash registers, more developers need to integrate it — but the specs are not published by acquirers and are only shared under NDA with point-of-sale software vendors. As a result, Italian developer communities have struggled for years without open documentation.

This library aims to fill that gap: it's the starting point we wished we had found. The framing was reconstructed from the public Nexi "Traditional POS" documentation, freely available, of the same ECR17 standard.

📖 Official (public) protocol reference: https://developer.nexigroup.com/traditionalpos/en-EU/docs/

Installation

Not a Composer package: simply copy src/Protocol17Client.php into your project and adapt the namespace as needed. Requires PHP 8.1+ (uses named arguments and match).

Quick usage

use Protocollo17\Pos\Protocol17Client;

$client = new Protocol17Client(
    host:           '192.168.1.100',  // POS terminal IP
    port:           10000,            // TCP port (often 10000)
    terminalId:     '00000000',       // POS TID; '00000000' = wildcard for testing
    cashRegisterId: '00000001',       // cash register ID, your choice
    lrcMode:        'stx',            // LRC mode (see below)
);

// Amount ALWAYS in cents: 100 = EUR 1.00
$result = $client->pay(100, function (string $status) {
    echo "POS status: $status\n"; // e.g. "Insert card", "Enter PIN"
});

if ($result['approved']) {
    echo "Approved! Auth code: {$result['auth_code']}\n";
} else {
    echo "Declined: {$result['description']}\n";
}

The lrcMode parameter (important!)

The LRC (control byte) is computed with an XOR starting from a base value of 0x7F (not 0x00 as in standard LRCs — this is the detail that blocks almost everyone). Which bytes enter the calculation depends on the terminal/acquirer. Four modes are provided:

lrcMode Calculation
stx 0x7F ^ STX ^ payload ^ ETX
std 0x7F ^ payload ^ ETX
noetx 0x7F ^ payload
stx_noetx 0x7F ^ STX ^ payload

Finding the right one: start with stx. If you get a NAK, try the others one by one with a minimal amount (EUR 1.00). When the POS display lights up asking for the card, you've found the correct mode for your terminal.

Result structure

For an approved transaction (result === '00') the array includes, among others: approved, pan, auth_code, read_type (ICC/MAG/CLM…), card_type, stan, datetime. For a declined transaction it includes description with the reason. The raw field is always present: it holds the raw payload, very useful for debugging and for verifying field positions on YOUR terminal.

POS configuration

Have the installer/acquirer confirm and set on the terminal:

  • Static IP (DHCP disabled)
  • Protocollo 17 enabled
  • Cash register IP = the IP of the machine running this code
  • TCP port (often 10000)
  • On some PAX devices: "Confirm amount from ECR" set to NO

Operational notes

  • One transaction at a time: the terminal handles a single active session.
  • Use minimal amounts in tests and handle reversals carefully.
  • readTimeout must cover the whole transaction (card wait + PIN + host): 90–120s.
  • Status messages (onStatus) are perfect for updating the cashier display in real time.

⚠️ Disclaimer

This is an independent, community project. It is NOT affiliated with, endorsed by, or supported by Nexi, PAX, Ingenico, UniCredit, or any acquirer or manufacturer. The protocol is not officially documented by acquirers; the correct lrcMode and field positions may vary between terminals and firmware versions. Test thoroughly with minimal amounts before any production use. The authors accept no liability for any damage, financial loss, or misbehaviour arising from the use of this software. Use at your own risk.

Questo è un progetto indipendente e community-driven. Non è affiliato, approvato o supportato da Nexi, PAX, Ingenico, UniCredit o alcun acquirer/produttore. Il protocollo non è documentato ufficialmente dagli acquirer; la lrcMode corretta e le posizioni dei campi possono variare tra terminali e versioni firmware. Testare a fondo con importi minimi prima di qualsiasi uso in produzione. Gli autori non si assumono alcuna responsabilità per danni, perdite economiche o malfunzionamenti derivanti dall'uso di questo software. Usare a proprio rischio.

📄 License

Released under the MIT License.

🤝 Contributing

Contributions are welcome! If you get this working on a terminal/acquirer not yet documented here, please open an issue or PR reporting the terminal model, the acquirer, and the lrcMode that worked. Building the compatibility table the community never had is the whole point.

Contributi benvenuti! Se lo fai funzionare su un terminale/acquirer non ancora documentato, apri una issue o una PR indicando modello del terminale, acquirer e lrcMode che ha funzionato. Costruire la tabella di compatibilità che alla community è sempre mancata è proprio lo scopo del progetto.

About

Client PHP per il protocollo italiano "Protocollo 17" / ECR17 di scambio importo cassa↔POS. The open documentation acquirers won't give you.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages