Elektronická evidence tržeb (EET) - SOAP komunikace rubrika: Programování: PHP

1 Teeto
položil/-a 2.7.2016

Zdravím,

s příchodem EET si ministerstvo vymyslelo nutnost elektronicky odesílat své tržby a to přes SOAP se kterým nemám žádné zkušenosti a vygooglit něco praktického je absolutně nemožné proto pokládám tento dotaz.

Ukázkový odeslaný a pozitivně přijatý dotaz vypadá následovně: http://www.etrzby.cz/assets/cs/prilohy/CZ00000019.valid.xml

Zpráva je v SOAP formátu a je tvořena obálkou, která obsahuje hlavičku pro meta data a body pro konkrétní data spojené s jednou tržbou.

Povedlo se mi vygenerovat tělo (soap:Body) zprávy pomocí jednoduchého kódu:

$result = $soap->OdeslaniTrzby([
    "Hlavicka" => [
        "dat_odesl" => "2016-09-19T19:06:37+01:00",
        "prvni_zaslani" => "false",
        "uuid_zpravy" => "9edeb22b-4234-4047-869c-3a76f86c20d3"],
    "Data" => [
        "celk_trzba" => "34113.00",
        "cerp_zuct" => "679.00",
        "cest_sluz" => "5460.00",
        "dan1" => "-172.39",
        "dan2" => "-530.73",
        "dan3" => "975.65",
        "dat_trzby" => "2016-01-05T00:30:12+01:00",
        "dic_popl" => "CZ00000019",
        "id_pokl" => "/5546/RO24",
        "id_provoz" => "273",
        "porad_cis" => "0/6460/ZQ42",
        "pouzit_zboz1" => "784.00",
        "pouzit_zboz2" => "967.00",
        "pouzit_zboz3" => "189.00",
        "rezim" => "0",
        "urceno_cerp_zuct" => "324.00",
        "zakl_dan1" => "-820.92",
        "zakl_dan2" => "-3538.20",
        "zakl_dan3" => "9756.46",
        "zakl_nepodl_dph" => "3036.00"
    ],
    "KontrolniKody" => [
        "pkp" => ["cipher" => "RSA2048", "digest" => "SHA256", "encoding" => "base64", "_" => $pkp],
        "bkp" => ["digest" => "SHA1", "encoding" => "base64", "_" => "1F1A2D90-4EAD34A8-411CFB0B-EB17616E-B2CE8114"]
    ]
        ]);

Problém je, že nedovedu vygenerovat hlavičku (SOAP-ENV:Header) zprávy. Respektive jak přidat dvojice atribut-hodnota. Vše programuji v php, nemáte s tímto někdo nějaké zkušenosti ?

Obecně lze řešení vytrollit přilepením html/xml textu namísto hlavičky ale určitě existuje nějaké lepší řešení, kde se přidávají dvojice atribut-hodnota jako to dělám v těle zprávy.

Předem děkuji za jakoukoliv pomoc.

Komentáře

  • Petr Voneš : V tomto případě by měl stát hlavně dodat plně funkční příklady komunikace, včetně řešení opakovaného odeslání traksance při chybě. 5.7.2016
  • tdvorak : @Petr Voneš: Co nespoléhat na stát a udělat ukázkové klienty jako FOSS? Neexistuje už něco takového? 5.7.2016
  • Teeto : Finanční správa ČR poskytuje pouze definice dat, nikoliv ukázky komunikace - pro člověka který nikdy nedělal se SOAP je to neřešitelný problém 5.7.2016
  • tdvorak : @Teeto: To není úplně pravda. Popis rozhraní obsahuje poměrně dost ukázek komunikace, dokonce kusy zdrojového kódu v Javě. Ale souhlasím, že SOAP je pro mnoho vývojářů problém. A bylo by fajn jim pomoct tím, že vzniknou ukázky klientských aplikací, které komunikaci řeší správně. Taková věc může běžet zcela mimo státní správu. Ostatně, mnoho firem bude tu implementaci muset udělat tak jako tak. A pokud budou mít k dispozici dobře napsané ukázky, třeba nesklouznou k lepení xml-podobných stringů. 5.7.2016
  • Petr Voneš : Tady ale nejde jen o základní SOAP. Je to ještě zabezpečené pomocí WS-Security (naštěstí, jiná ministerstva dokonce vymýšlela své vlastní nestandardní paskvily), což tím dřevním způsobem matláním stringů podle příkladu (jako JSON) je téměř neřešitelné. To ale není chyba té technologie, ta už je mnoho let známá. 5.7.2016
odkaz Vyřešeno
3 Ondrejj
odpověděl/-a 16.7.2016
 
upravil/-a 16.7.2016

Řeším EET v PHP
Žádné lepení xml stringu není potřeba! Na vše existují knihovny:

Soap klient: integrovaný v PHP (http://php.net/manual/en/class.soapclient.php)
umí sestavit XML na základě WSDL (akorát IDE nenašeptává metody, v tom je .NET dál)

Soap Header:
Podepsat tělo soapu umí knihovna https://github.com/robrichards/wse-php
má závislost na https://github.com/robrichards/xmlseclibs
v podstatě lze vyjít z tohoto examplu a jen vyměnit algoritmy dle specifikace EET https://github.com/robrichards/wse-php/blob/master/examples/ws-amazon.php

Vygenerovani PKP podpisu:
lze řešit rovněž přes xmlseclibs třídou XMLSecurityKey

Jsem velice mile překvapen, že statní správa zvolila dlouhodobě zavedené standardy SOAP/WSDL/XSD/WS-security. Je to nejlepší možné řešení a pro nás snadné na implementaci.
Dokumentace je skvělá, přestože je WSDL samo popisující, tak jej přepsali do lidské podoby a přidali ukázky volání a ukázky kódu.

Komentáře

  • tdvorak : Bylo by případně možné publikovat tvou implementaci jako OSS? Určitě by to spoustě lidí pomohlo (a zlepšilo kvalitu dalších PHP implementací). 14.7.2016
  • michal.svojanovsky : Přimlouvám se za ukázku implementace, chápu že je to pracné a jsem ochoten i zaplatit (pokud to bude spolehlivě funkční). 15.7.2016
  • kajacx : Díky za odkazy na knihovny, určitě to pomůže. Jinak kde jsi prosím našel ty ukázky volání? "..., tak jej přepsali do lidské podoby a přidali ukázky volání a ukázky kódu." Já nikde na té jejich technické specifikaci nenašel jediný příklad, jak tu SOAP zprávu generovat (teda tělo je tam popsané dobře, ale k hlavičkám je pouze odkaz na officalní web soapu a WS-security, takže žádný ukázkový kód). 16.7.2016
  • harrison314 : @kajacx: pretoze ws-security a ws-adresing je standard, tak pouzijes standardne kniznice, uz len parsovat rucne o je o drzku, nie este skladat 16.7.2016
  • Teeto : také bych se přidal k prosbě upravit odkazovaný kód na ws-amazon pro potřeby EET, osobně se mi absolutně nedaří vygenerovat SOAP hlavičku a dle tohoto postu se to zdá být triviální záležitostí 18.7.2016
  • dzejkob : Když to tak čtu - tak v tom amazon requestu stačí vyhodit "$objWSSE->addTimestamp();" - potom změnit XMLSecurityKey::RSA_SHA1 na XMLSecurityKey::RSA_SHA256 a volání ->signSoapDoc upravit takto: ->signSoapDoc($objKey, array("algorithm" => XMLSecurityDSig::SHA256)) - potom by to mělo vyhovovat security policy eet. Potom je třeba tomu dodat pem klíč a pem certifikát + doplnit pkp a bkp dle specifikace (na to patrně openssl funkce v php). 18.7.2016
  • Teeto : děkuji za info, určitě se hodí, nicméně problém s generováním hlavičky stále přetrvává, abych nechodil kolem horké kaše tak tady je můj zdrojová soubor ve kterém se snažím napodobit volání amazon requestu upravené pro EET pastebin.com/CXq6tpY7 a výsledek (vygenerovaný request) je následovný pastebin.com/eFLgN7Ph bez SOAP hlavičky. Jediný problém snad může být v té závislosti použité knihovny na druhé, kterou uvádí autor příspěvku, nicméně se mi nikde při běhu neprojevila žádná chyba s chybějícím souborem takže to může být problém 18.7.2016
  • dzejkob : No ještě pro mne není aktuální to řešit v php - takže zcela přesně neporadím. Ovšem za prvé je třeba někam vložit jak vypadá výsledné soap XML - což patrně umí tato funkce: http://php.net/manual/en/soapclient.getlastrequest.php , za druhé jako PRIVATE_KEY je možné, že to nepobere pkcs - takže bych tam zkusil dát vyexportovaný klíč v pem - a CERT_FILE je určitě špatně - tam má být certifikát obchodníka, kterým se ten podpis ověřuje - ten je obsažen taktéž v balíčku pkcs (.p12) - takže bych zkusil buď na oboje místa dát 01000004.p12 - a pokud to podpis nevygeneruje, tak je nutný to zkonvertovat do pem. 18.7.2016
  • dzejkob : Tak přehlédnul jsem - ten request byl v druhém odkazu. Ve zdrojáku toho "loadKey" to používá http://php.net/manual/en/function.openssl-pkey-get-private.php nebo get_public - a obojí načítá PEM klíč. Je možný, že tahle funkce http://php.net/manual/en/function.openssl-pkcs12-read.php načte to pkcs a výsledný certifikát a klíč je PEM. Takže výsledek někam uložit a pak to nacpat do těch konstant (nebo upravit loadKey). 18.7.2016
  • dzejkob : Poslední dodatek - heslo toho pkcs klíče je "eet". 18.7.2016
  • Ondrejj : __getLastRequest() vraci xml bez hlavicky. Dumpni si $objWSSE->saveXML(), PRIVATE_KEY musi byt v PEM formatu (jde pres openssl zkonvertovat z pk12 do PEM) 18.7.2016
  • Ondrejj : Zveřejnuji můj example: https://github.com/ondrejnov/eet PKP/BKP je bez záruky, zdá se, že testovací prostředí příjme i evidentně chybný PKP/BKP 18.7.2016
  • dzejkob : Kontrolují jenom BKP - a neboť vychází z PKP, tak musí být taktéž správně. Možná by nezkontrolovali, kdyby nebylo pkp base64 kódované - ovšem to by pak nemuselo být validní xml. 18.7.2016
  • Ondrejj : pokud sestavim retezec k podpisu PKP z jinych hodnot nez jsou v sekci Data, tak bych ocekaval, ze mi zprava neprojde. 18.7.2016
  • dzejkob : Tak to jsem nezkoušel. Pokud ale bude bkp z jiného zdroje než je podepsané pkp, tak to vrátí "nesouhlasí kontrolní kód poplatníka (bkp)" nebo tak něco. Víc chybových stavů zatím specifikováno nemají (kromě těch ohledně komunikace a validace). 18.7.2016
  • tdvorak : @Ondrejj: Díky za zveřejnění a MIT licenci, vypadá to super! Ještě jeden dotaz - kontroluješ i podpis odpovědi (certifikát EET od I.CA, případně i oproti CRL)? 20.7.2016
  • Ondrejj : Podpis teď nekontroluji. Ve specifikaci je povinnost kontrolovat TLS certifikát, ale o kontrole soap response nic neříkají. Taky nevím, co bych dělal, když ti přijde zaevidovaný FIK s neplatným soap podpisem. Jestli to je duvod k nepredani FIK zakaznikovi nebo jestli odeslani uctenky opakovat, a kdyz opakovat jak jestli to povazovat na druhe odeslani nebo za prvni. 20.7.2016
  • ic : PHP to má docela podchycené, ale kromě SOAPu není ještě jiná možnost komunikace? Myslím třeba pro javascript žádný soap klient není :( to mají Javascripťáci v tomto směru smůlu ? 6.9.2016
  • dzejkob : No opět google potvrdil, že vše co člověka napadne hledat, tak najde: https://github.com/travist/jsencrypt, https://github.com/yaronn/xml-crypto 6.9.2016
  • david.macek3 : Diky moc, podarilo se mi to rozchodit v playground, ale dnes jsem si stahnul ostry certifikat z danovyho portalu a asi jsem uplne natvrdlej, ale nejak nechapu, proc mi to nefunguje. Muzu poprosit nekho, aby mi vysvetlil, co mam udelat se stazenymi soubory 318999283.crt, 318999283.p12, cacert.crt a current.crl? Pro Playground jsem pouzil soubory eet.key a eet.pem... Nebo musim pockat do listopadu nez rozjedou ostrej server? Stejne pak ale nebudu tusit, co s temi stazenymi soubory... Ps: muzu prispet nejak jinak nez pres bitcoin? 7.9.2016
  • Ondrejj : pres funkci openssl_pkcs12_read() muzes soubor .p12 rozlozit na verejny a soukromy klic. Prispet muzes i tak, ze podporu pk12 implementujes do knihovny a posles pull request ;) 7.9.2016
  • MartinK : Děkuji @Ondrejj za uveřejnění. Vše funkční, skvělé. Pro testovací certifikáty ok. Nyní ostrý certifikát .p12, ten když rozložím na CERTIFICATE a PRIVATEKEY, a nahradím načítání testovacího certifikátu, tak dostávám zpět odpověď "Neplatny podpis SOAP zpravy". Je potřeba posílat pro ostrý certifikát ve zprávě nějaké jiné údaje? Nebo v čem jiném by mohl být problém? 9.9.2016
  • ubuster : @MartinK: Ostré certifikáty, které jsou vydávány poplatníkům pro evidování tržeb, nejsou určeny pro použití na testovacím prostředí (Playground). V testovacím prostředí lze použít pouze certifikáty, které jsou zveřejněny na webu v sekci IT/Vývojář. 15.9.2016
  • ja13 : @Ondrejj: když jedu na PHP 5.5, tak mi ta tvoje knihovna nebude fungovat? 2.10.2016
  • ja13 : Teeto: Dzejkob: ostatní: Prosím, již někdo vyřešil to generování té hlavičky? Marně hledám v diskuzi, ale nenacházím. odkaz:http://pastebin.com/pbfiBtBD 4.10.2016
  • Anonym : nemohl by někdo udělat nějaké php řešení pro lamy? :-) že bych prostě jen někam nastavil cestu k mým certifikátům a do proměnných dosadil požadované hodnoty potřebné pro odeslání a zpět by mi přišel kód v proměnné, kterou bych si pak uložil do DB :-) jsem takový php amatér, ale to EET bych potřeboval rozchodit, mám pár svých systémů udělaných právě v php a bude to podléhat EET příští rok a popříští.... Jsem ochoten samozřejmě i pustit korunu, kdyby někdo to pro mne udělal a bylo by to funkční. Kdyžtak prosím na mail admin/zavináč/smhard.cz. 22.10.2016
  • k.korous : zkouším implementaci github.com/ondrejnov/eet a při volání testovacího případu se dostanu k tomuhle výstupu object(SoapFault)#23 (10) { ["message":protected]=> string(56) "Call to undefined function RobRichards\XMLSecLibs\hash()" instalovall jsem to přes composer RobRichards knihovna tam je. Díky edit: pro FreeBSD je PECL-Hash podporovaný pouze pro PHP4. Řeší to modul php56-hash-5.6.27 12.11.2016
  • meca.petr : Děkuji @Ondrejj za uveřejnění, vše funguje. Opravdu mi to pomohlo. Jen upozornění, že na účtenku je třeba dát i BKP. Tvá funkce "send" vrací jen FIK. A také pokud by došlo k výpadku serveru finančáku, tak je třeba na účtenku dát PKP (protože FIK neexistuje) a to ta funkce taky nevrací. Vše už si tam doplním sám, ale dávám jen info i pro ostatní. 26.11.2016
  • Ondrejj : @meca.petr: kody BKP/PKP lze vypočítat offline před odesláním, proto se domnívám, že by nemělo být součástí metody send(), ale jiné public metody getCheckCodes(). 26.11.2016
  • lukame : Nemáte někdo kód jak pres funkci openssl_pkcs12_read() soubor .p12 rozlozit na verejny a soukromy klic? Stále mám prázdné soubory. 28.11.2016
  • Tom_as : Dobrý den Ondřeji, zajímalo by mě jak v php řešení řešíte vypočítání BKP/PKP offline? Musí se přeci vracet nejen v případě, že je výpadek serveru finančáku ale i případě, že je výpadek vašeho serveru. Děkuji :) 30.11.2016
  • Ondrejj : tom: k výpočtu bkp a pkp stačí údaje z účtenky a podpisový certifikát. Toto lze spočítat i bez připojení k internetu. Algoritmus je v EET dokumentaci. 30.11.2016
  • Tom_as : Ano ale co jsem koukal tak vše počítáte v php a to přeci při výpadku serveru na kterém to běží nepůjde. Jediné co mě napadlo je v takovém případě vracet výsledek v jiném jazyce. 30.11.2016
  • Ondrejj : Dal jsem k dispozici kód pro PHP. Je na každém jestli a kam ho nasadí. Jestli bude hostovat skript na sdíleném hostingu na Wedosu nebo použije nějaké HA řešení, kde výpadek serveru nebude znamenat komplikace. PHP lze spouštět i bez webserveru jako CLI. Lze mít skript na lokálním Raspberry PI... Kdo chce použít jiný jazyk, tak již existují alternativní knihovny https://github.com/todvora/eet-client#similar-projects 30.11.2016
  • michalj : Zdravím, zkouším implementaci ondrejnov/eet. Když se pokouším vypsat PKP tak jsou v něm netisknutelné znaky (otazníky). V některých případech se tiskne na účtenku. Můžete poradit co s tím? Díky 12.2.2017
  • jank : @michalj: použije se funkce base64_encode() a vytiskne se pak těch 344 znaků. 13.2.2017
  • michalj : díky moc! 13.2.2017

Pro zobrazení všech 15 odpovědí se prosím přihlaste:

Rychlé přihlášení přes sociální sítě:

Nebo se přihlaste jménem a heslem:

Zadejte prosím svou e-mailovou adresu.
Zadejte své heslo.