EET 2.0: chyba 4 „Neplatny podpis SOAP zpravy“ a jak ji najít

Pro vývojáře pokladních systémů · rozhraní EET v4 · 21. 10. 2026

Kód 4 je kritická chyba: server zprávu odmítne a POK nevydá. Podle specifikace (kap. 2.2.3) se kontroluje vydavatel a platnost pokladního certifikátu včetně CRL a kryptografická platnost podpisu. Tady je osm nejčastějších příčin, seřazených podle toho, jak často na ně narazíte.

Nejrychlejší cesta: vložte svou zprávu do validátoru. Spočítá DigestValue a ověří SignatureValue stejně jako server a ukáže kanonický tvar. Běží v prohlížeči, zpráva nikam neodchází.

1. Certifikát a prostředí nesedí

Testovací certifikát (vydavatel playground EETv2 NCA SubCA) funguje jen na pg.trzbyeet.gov.cz, produkční certifikát z DIS+ jen na trzbyeet.gov.cz. Záměna vrací kód 4, ne srozumitelnější hlášku.

2. Podepisujete jiný text, než posíláte

Nejčastější chyba v praxi. Podpis se počítá nad kanonickým tvarem soap:Body, ale SOAP knihovna, šablona nebo „pretty print“ zprávu po podpisu přeformátuje: přidá mezery a odřádkování, změní pořadí atributů nebo přesune deklarace jmenných prostorů. Pravidlo: serializovat jednou, podepsat a poslat přesně ty bajty.

3. InclusiveNamespaces a PrefixList

Vzorová zpráva FS používá exkluzivní kanonizaci s InclusiveNamespaces PrefixList="v4" pro Body a "soapenv v4" pro SignedInfo. Pokud PrefixList v XML deklarujete, musíte ho použít i při výpočtu, a naopak. Prefix v4 se pak vykreslí už na soap:Body, ne až na v4:Trzba, a otisk je jiný.

<soapenv:Body xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
              xmlns:v4="http://fs.gov.cz/eet/schema/v4"
              xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
              wsu:Id="id-…"><v4:Trzba>…

Kanonický tvar Body podle vzoru FS: deklarace seřazené podle prefixu, pak atributy, prázdné prvky jako pár značek <a></a>.

4. Podepsaný je jiný element nebo víc elementů

Podepsat se musí právě jeden element: soap:Body (kap. 5.2). Knihovny WS-Security často přidávají wsu:Timestamp a podepisují i ten. Specifikace výslovně doporučuje žádné další hlavičky. Timestamp i WS-Addressing vypněte.

5. Reference neukazuje na Body

ds:Reference URI="#id-…" musí odpovídat wsu:Id na Body a jmenný prostor wsu musí být správný (…oasis-200401-wss-wssecurity-utility-1.0.xsd).

6. Špatné algoritmy

Vyžaduje se xml-exc-c14n# pro kanonizaci i transformaci, xmlenc#sha256 pro otisk a xmldsig-more#rsa-sha256 pro podpis. Výchozí nastavení starších knihoven bývá SHA-1.

7. Certifikát vypršel nebo je zneplatněný

Pokladní certifikát platí 366 dní a FS kontroluje i CRL. Zneplatnění v DIS+ je okamžité a nevratné. Pozor na záměnu: vygenerování nového certifikátu starý nezneplatní, ale ruční zneplatnění (třeba omylem jiného certifikátu) ano.

8. Obsah se změnil až po podpisu

Typicky formátování částky (100 → 100.00) nebo času v jiné vrstvě aplikace, která běží až po podpisu. Validujte a normalizujte data před sestavením zprávy. Formátové chyby částek mimochodem vracejí kód 3 (XML schéma), ne 4.

Jak to debugovat systematicky

  1. Ověřte si implementaci na oficiální vzorové zprávě CZ00000019.eet.v4.req.xml z eet.gov.cz: vaše kanonizace jejího Body musí dát stejný DigestValue a SignatureValue ze vzoru musí být platná nad vaším SignedInfo.
  2. Logujte přesně odeslané bajty a hlavičku odpovědi X-Global-Transaction-Id. Podpora FS (epodpora@fs.gov.cz) podle ní dohledá konkrétní pokus.
  3. Než pošlete ostrou tržbu, pošlete zprávu v ověřovacím módu (overeni="true"). Server odpoví kódem 0 a nic neeviduje.

Nechte podpisy na nás

Naše knihovna @evidka/eet2 má kanonizaci ověřenou proti vzorové zprávě FS. Evidka brána pak podpis, frontu i doposílání vyřeší za vás: posíláte jen JSON.

Dokumentace API Integrace na klíč

Zdroj: Finanční správa – „Formát a struktura údajů o evidované tržbě a popis datového rozhraní“ v1.2 (25. 8. 2026), kap. 2.2.3, 3.5.4 a 5.2; vzorové zprávy na eet.gov.cz/cs/pro-vyvojare.