EET 2.0 v Node.js a TypeScriptu

Pro vývojáře · rozhraní EET v4 · knihovna @evidka/eet2 (MIT) · 21. 10. 2026

Knihovnu zatím posíláme na vyžádání (info@evidka.cz), zveřejnění na npm připravujeme. Bez knihovny stačí jeden HTTP požadavek na Evidka API.

EET 2.0 posílá každou tržbu jako SOAP 1.1 zprávu podepsanou podle WS-Security. Pro PHP a .NET už knihovny existují, pro JavaScript/TypeScript jsme žádnou nenašli. Tady je kompletní postup v Node.js 20+ (a v prohlížeči, protože FS podporuje CORS).

Instalace a první tržba

import { readFile } from 'node:fs/promises';
import { EetClient, signerFromP12 } from '@evidka/eet2';

// .p12 a heslo z DIS+ (Správa pokladních certifikátů EET)
const signer = await signerFromP12(await readFile('certifikat.p12'), process.env.EET_HESLO);
console.log(signer.info); // EIČ, platnost, playground ano/ne

const eet = new EetClient({ signer, prostredi: 'playground', timeoutMs: 3000, overitOdpoved: true });

const r = await eet.odeslat({
  eicPopl: signer.info.eic,   // EIČ = DIČ
  idJednotky: 303,            // číslo evidenční jednotky z DIS+
  idPokl: 'KASA-1',           // max 20 znaků, unikátní v rámci provozovny
  poradCis: '2027/15',        // číslo účtenky, max 25 znaků
  celkTrzba: '349,00',        // Kč – číslo, řetězec i bigint (haléře)
});

if (r.stav === 'prijato') console.log('POK', r.pok, r.podpisOdpovedi);

Co knihovna řeší za vás

Ověřovací mód

Než pošlete první ostrou tržbu, ověřte spojení. Server zprávu zkontroluje a vrátí kód 0, tržba se neeviduje:

const test = await eet.odeslat(trzba, { overeni: true });
// test.stav === 'overeno'  → certifikát, podpis i formát v pořádku

Výpadky a doposlání do 48 hodin

Zákon (§ 19–20) chce mezní dobu odezvy aspoň 2 sekundy. Když server neodpoví, musíte tržbu doposlat do 48 hodin, s novým UUID zprávy a příznakem prvni_zaslani=false. Data tržby zůstávají stejná, aby je FS spárovala. FS má navíc pravidelné servisní okno (zpravidla čtvrtek 20:00–6:00).

switch (r.stav) {
  case 'prijato':    ulozitPok(r.pok); break;
  case 'nedoruceno': fronta.push(r.zprava.etrzba); break;              // timeout, síť
  case 'chyba':      if (r.opakovat) fronta.push(r.zprava.etrzba);    // kód -1
                     else alert(`${r.kod}: ${r.text}`); break;         // kritická chyba
}

// worker – doposlání
for (const etrzba of fronta) {
  const znovu = await eet.odeslatZnovu(etrzba); // nové UUID, prvni_zaslani=false
}

V prohlížeči bez serveru

Společné technické zařízení FS posílá CORS hlavičky (kap. 2.3.3 specifikace), takže webová pokladna může posílat přímo. Klíč jde importovat jako neexportovatelný CryptoKey a uložit do IndexedDB. Ani skript ho pak nevytáhne:

import { loadP12, importPrivateKey, createSigner, EetClient } from '@evidka/eet2';
const { privateKeyPkcs8, certificateDer } = loadP12(await soubor.arrayBuffer(), heslo);
const klic = await importPrivateKey(privateKeyPkcs8, false);
const eet = new EetClient({ signer: createSigner(klic, certificateDer), prostredi: 'produkce' });

Obnova certifikátu přes API CA EET

Pokladní certifikát platí 366 dní. CA EET má REST API pro automatickou obnovu: JWT (RS256, hlavička x5t#S256) podepsaný starým certifikátem.

import { CaEetClient, CA_EET_API } from '@evidka/eet2';
const ca = new CaEetClient({ signer, baseUrl: CA_EET_API.produkce });
const novy = await ca.obnovit();          // žádost → čekání → stažení .p12 a hesla
await bezpecneUlozit(novy.p12, novy.heslo);
await ca.potvrditStazeni(novy.reqId);     // až po uložení – pak už nejde stáhnout znovu

Nechcete to provozovat? Evidka brána dělá totéž jako služba: pošlete JSON, dostanete POK, frontu, doposílání, webhooky i obnovu certifikátů. Dokumentace API.

Ladění

Když server vrací chybu 4 – Neplatný podpis, vložte odesílanou zprávu do validátoru. Spočítá otisk i podpis stejně jako server.

Zdroje: „Formát a struktura údajů o evidované tržbě a popis datového rozhraní“ v1.2; „CA EET v2.0 – Postupy získání pokladního certifikátu“; caeetapi_jwt.yml; zákon č. 180/2026 Sb.