Bankovní integrace¶
Fiktivní údaje
Všechny ukázky v této dokumentaci (příklady e-mailů, čísla účtů, jména, zůstatky) jsou smyšlené a slouží jen jako ilustrace formátu. Zadávejte vždy vlastní data.
Podporovaní poskytovatelé¶
| Typ | Poskytovatel | Autentizace | Interval kontroly | Poznámky |
|---|---|---|---|---|
| API | Fio Banka | Fio API token (FIO_TOKEN) |
Každých 11 minut | Jednodušší nastavení |
| API | Erste / ČSAS | OAuth2 (Authorization Code) | Každých 11 minut | Vyžaduje registraci na developer portálu |
| E-mail (IMAP) | Bankovní e-mailová avíza | IMAP e-mail + heslo / aplikační heslo | Dle PAYMENT_CHECK_INTERVAL_MINUTES (výchozí 11 minut) |
Pro banky bez veřejného API; systémové parsery + vlastní regex providery |
| — | Dummy | Žádná | — | Pro testování |
Aktivace poskytovatele¶
Aktivní poskytovatel se volí tlačítkem Aktivovat v záložce Integrace (POST /api/v1/admin/integrations/activate, tělo {"provider": "fio"}, prázdný řetězec = vypnout). Seznam povolených poskytovatelů se odvozuje z registru poskytovatelů (každý poskytovatel deklaruje svá konfigurační data), testovací dummy aktivovat nelze.
Při aktivaci se kontroluje úplnost nastavení (jen při aktivaci; již aktivní poskytovatel běží dál beze změny):
| Poskytovatel | Musí být nastaveno |
|---|---|
fio |
FIO_URL, FIO_TOKEN |
erste |
ERSTE_API_KEY, ERSTE_CLIENT_ID, ERSTE_CLIENT_SECRET (URL mají výchozí hodnotu) |
email |
INCOMING_PAYMENT_NOTIFICATION_IMAP_HOST, INCOMING_PAYMENT_NOTIFICATION_EMAIL_ADDRESS |
Při chybějících klíčích endpoint vrátí 400 s výčtem chybějících klíčů a nic nezapíše. Hodnoty se bere z prostředí i z databáze. Vypnutí ("") funguje vždy.
Nový poskytovatel se přidává novým souborem: RegisterProvider + RegisterProviderMeta (klíče, povinné klíče). Klíče poskytovatele musí být v registru konfigurace se skupinou payment a znovuinicializací platební služby — kontroluje to TestRegistryCompletenessPaymentProviders.
Fio Banka¶
Jednoduché napojení — stačí nastavit konfigurační klíče:
-
V administraci nastavte:
PAYMENT_CHECK_SERVICE_ENABLED=truePAYMENT_CHECK_SERVICE_NAME=fioFIO_URL=https://fioapi.fio.cz/v1FIO_TOKEN= váš API token z Fio internetového bankovnictví
-
Cron úloha automaticky kontroluje transakce každých 11 minut.
Erste / Česká spořitelna (ČSAS)¶
Erste používá OAuth2 Authorization Code flow s krátkodobými přístupovými tokeny (5 min) a dlouhodobými obnovovacími tokeny (~180 dnů).
Předpoklady¶
- Zaregistrujte se na Erste Developer Portal
- Vytvořte aplikaci s přístupem k "Accounts" API
- Nastavte redirect URI:
- Produkce:
https://vaše-doména.cz/bank/erste/redirectFromBank - Vývoj:
http://localhost:8090/bank/erste/redirectFromBank
- Produkce:
- Poznamenejte si API Key, Client ID a Client Secret
Nastavení¶
-
V administraci nastavte:
PAYMENT_CHECK_SERVICE_ENABLED=truePAYMENT_CHECK_SERVICE_NAME=ersteERSTE_API_KEY= váš API key (WEB-API-key)ERSTE_CLIENT_ID= váš OAuth2 Client IDERSTE_CLIENT_SECRET= váš OAuth2 Client SecretERSTE_IDP_URL= URL IDP (výchozí je sandbox)ERSTE_API_URL= URL Accounts API (výchozí je sandbox)
-
Přejděte na záložku Integrace v administraci
-
Klikněte na Připojit u karty Erste
-
Budete přesměrováni na přihlašovací stránku banky. Přihlaste se a schvalte souhlas.
-
Po přesměrování zpět vyberte bankovní účet pro párování transakcí.
-
Hotovo! Cron úloha kontroluje transakce každých 11 minut.
Průběh OAuth2¶
Administrátor klikne "Připojit"
|
v
POST /api/v1/admin/bank/erste/connect
|
| Vrací auth_url
v
Prohlížeč přesměruje na Erste IDP
(admin se přihlásí, schválí souhlas)
|
v
GET /bank/erste/redirectFromBank?code=...&state=...
|
| Ověří state (CSRF ochrana)
| Vymění code za access_token + refresh_token
| Uloží tokeny do kolekce oauth_tokens
| Automaticky vybere účet, pokud existuje jen jeden
v
Přesměrování na /admin?integration=erste&status=success
Správa tokenů¶
- Access token vyprší každých 5 minut (300 s)
- Refresh token vyprší za ~180 dnů (15 552 000 s)
- Tokeny se obnovují na požádání před každým API voláním
- Když refresh token vyprší → stav se změní na "expired" → administrátor musí znovu připojit
- Všechny tokeny uloženy v kolekci
oauth_tokens
Sandbox vs Produkce¶
| Nastavení | Sandbox | Produkce |
|---|---|---|
ERSTE_IDP_URL |
https://webapi.developers.erstegroup.com/api/csas/sandbox/v1/sandbox-idp |
https://webapi.developers.erstegroup.com/api/csas/production/v1/idp |
ERSTE_API_URL |
https://webapi.developers.erstegroup.com/api/csas/public/sandbox/v3/accounts |
https://www.csas.cz/webapi/api/v3/accounts |
Řešení problémů¶
| Problém | Řešení |
|---|---|
| "Token expired" | Refresh token vypršel (~180 dnů). Klikněte "Znovu připojit" v záložce Integrace |
| "No account selected" | Přejděte do záložky Integrace a vyberte bankovní účet |
| OAuth callback chyba | Ověřte, že redirect URI přesně odpovídá tomu, co je registrováno na Erste Developer Portal |
Jak párování funguje¶
Systém páruje platby podle:
- Variabilní symbol (VS) — unikátní číslo dobíjení nebo nákupu
- Specifický symbol (SS) — hash ID uživatele
- Částka — musí přesně odpovídat
U plateb za relaci musí částka odpovídat amount_due (kolik zbývá
doplatit), ne celé ceně relace. Pokud uživatel část uhradil kreditem, QR kód
i párování počítají jen se zbytkem.
Po spárování se automaticky:
- Dokončí dobíjení → připíše se kredit
- Aktivuje produkt → začne platit
- Uhradí relace → do rozpisu se zapíše úhrada ve výši zbývající částky
(kredit 300 + převod 518 → úhrada −518),
amount_dueklesne na 0 afinal_pricezůstane 818
Spárovaná platba: proč nemazat řádek platby
Řádek platby (bank_tx:*, manual, kredit) v rozpisu relace
nikdy nemažte ručně v databázi. Smazání nespustí žádný přepočet,
relace zůstane paid, ale při další úpravě rozpisu (např. ruční úpravě
ceny) se přepočítá na nezaplacenou a uživateli vznikne fantomový dluh
včetně upomínek. Opravy dělejte přes akce v administraci (ruční úprava
ceny, označit jako zaplaceno).
Starší relace
Migrace 1791200000_session_amount_due automaticky přepočítá všechny
existující relace z jejich rozpisu (final_price, discount,
amount_due, paid) — ruční oprava databáze není potřeba. Již zaplacená
relace se migrací nikdy nevrací na nezaplacenou.
Přehled bankovních transakcí¶
Všechny načtené bankovní transakce (Fio API, Erste API i e-mailová avíza) se ukládají do jednotné auditní tabulky bank transactions v administraci (Nastavení → Integrace → Bankovní transakce). Vidíte v ní, které platby se spárovaly s dobíjením / produktem / rezervací a které zůstaly nespárované.
Jak se transakce zapisují¶
- Orchestrátor (automatický cron, ruční „Kontrola plateb" nebo „Skenovat
nyní") načte transakce z banky a všechny je zapíše jako
unmatched - Párovací engine (stejné sezení/dobíjení/produkty jako u API providerů)
vyhledá spárovatelné transakce a upgraduje je na
matchedse záznamem, k čemu jsou připárované (matched_to_type+matched_to_id) - Překrývající se načtení (Fio vrací celé dny, opakované běhy) nikdy nevytvoří duplicitní řádek — deduplikace probíhá vždy
Deduplikace a ochrana peněz¶
- Dedup klíč =
provider:external_id; když API external ID neobsahuje (některé banky), použije se fingerprintprovider:datum:částka:vs:ss— stejná fyzická platba se tak vždy mapuje na stejný řádek - Unique index na
dedup_key— duplikát se nikdy nevytvoří - Stav
matchedje finální: jednou spárovaná transakce se nikdy nevrátí naunmatchedani se nepřepáruje na jinou entitu. I když ji příští cron běh načte znovu jako „novou", stav zůstávámatched
Tabulka v UI¶
Karta Bankovní transakce obsahuje sloupce Datum, Provider, Částka, VS, SS, Protistrana, Stav a „Připárováno k" s možností:
- Filtrování — podle provideru (
fio/erste/email), podle stavu (matched/unmatched) a textové vyhledávání (VS, SS, external ID, protistrana) - Řazení — kliknutím na hlavičku sloupce (Datum, Provider, Částka, VS, SS, Stav); výchozí řazení podle Datum (nejnovější nahoře)
- Stránkování — 50 záznamů na stránku s navigací
« 1 z N » - Detail — tlačítko Detail u řádku rozbalí podrobný pohled včetně
raw_data(kompletní odpověď banky / rozparsované avízo)
API¶
GET /api/v1/admin/bank/transactions— seznam (admin only). Podporujepage,perPage(max 200),provider,status,searchasort(povolené sloupce:first_seen_at,matched_at,created,amount,status,provider,vs,ss,date,external_id; neplatné hodnoty se tiše ignorují). Seznamraw_datanevracíGET /api/v1/admin/bank/transactions/{id}— detail (admin only), včetněraw_data
Kolekce bank_transactions má všechna pravidla přístupu prázdná (nil) —
pod data se dostanete výhradně přes admin endpointy výše.
E-mailová (IMAP) platební avíza¶
Pro banky bez veřejného API (menší banky, stavební spořitelny) lze platby párovat z e-mailových avíz, která banka posílá na vaši schránku. Systém se připojí přes IMAP, načte e-maily, rozpozná banku podle odesílatele (a podle těla e-mailu), extrahuje variabilní symbol a částku a předá je stejnému párovacímu enginu jako API poskytovatelé.
Podporované e-mailové providery¶
Systémové parsery (registrované v kódu, v internal/bankemail/):
| Banka | Kód provideru | Doména odesílatele | Avízo |
|---|---|---|---|
| Fio banka | fio |
fio.cz |
Přišel/odešel příjem na kontě |
| ČSOB | csob |
csob.cz |
Moje info Avízo |
| ČSOB CEB | csob_ceb |
csob.cz |
CEB Info: Zaúčtování platby |
| UniCredit Bank | unicredit |
unicreditgroup.cz, unicredit.eu |
Informace o pohybu na účtu |
| Raiffeisenbank | raiffeisenbank |
rb.cz |
Pohyb na účtě |
| MONETA Money Bank | moneta |
moneta.cz |
Přišly/Odešly peníze |
| CREDITAS | creditas |
creditas.cz |
Notifikace o změně na účtu |
| Air Bank | airbank |
airbank.cz |
Zvýšení/Snížení zůstatku |
| Komerční banka | kb |
kb.cz |
Oznámení o platbách |
| Česká spořitelna | csas |
— | Přišla/Odešla platba |
Banky, které v tabulce nejsou, lze přidat bez změny kódu jako vlastní
regex providery (BANK_EMAIL_REGEX_PROVIDERS) — viz sekci níže.
Živý přehled parserů v UI
Administrátor si může v Nastavení → Bankovní integrace → E-mail → Přehled
parserů otevřít u každého parseru jeho routingová kritéria (domény
odesílatele, podmínky na předmět a tělo, vč. regexů) a vzory polí pro
extrakci plateb. Přehled se generuje přímo z kódu parserů
(Info()/Criteria), takže se nikdy nemůže rozejít se skutečným
routováním. Tabulka výše je jen rychlý přehled; autoritativní zdroj je UI.
Rozlišení csob vs csob_ceb (obě domény csob.cz): csob vyžaduje
v předmětu „moje info" + „avízo" a v těle „parametry platby" a „čsob",
csob_ceb vyžaduje v předmětu „ceb info" a v těle „zaúčtován" +
„variabilní symbol" — podmínky se vzájemně vylučují, ke kolizi nedochází.
Kdy použít¶
| Situace | Doporučení |
|---|---|
| Banka má API (Fio, Erste/ČSAS) | Použijte API poskytovatele |
| Banka API nemá, avíza chodí e-mailem | Použijte e-mailový poskytovatel |
| Chcete ověřit, že avíza opravdu chodí | Nastavte e-mail a použijte tlačítko Skenovat nyní |
Předpoklady¶
- E-mailová schránka, která přijímá avíza a podporuje IMAP
- Přihlašovací údaje pro IMAP (heslo, případně aplikační heslo)
- Avíza mají ve zprávě variabilní symbol a částku
Přesný název složky
Název složky se nemusí shodovat s tím, co vidíte ve svém e-mailovém klientovi — klienti často zobrazují lokalizované nebo upravené názvy. Autoritativní názvy zná jen IMAP server. Použijte tlačítko Procházet složky v kartě bankovního e-mailu — vypíše přesné názvy přímo ze serveru a kliknutím na složku ji nastavíte.
Aktivace avíz u banky¶
Dřív než systém může avíza číst, musí je banka začít posílat na schránku, kterou systém sleduje. Postup se liší banka od banky a nastavuje se výhradně v internetovém bankovnictví dané banky (ne v Dverniku).
Česká spořitelna (csas)¶
Česká spořitelna zasílá avíza Přišla/Odešla platba na e-mail, který si vyberete v internetovém bankovnictví. Nastavení je dostupné pouze ve webové aplikaci — v mobilní aplikaci ho nejde aktivovat.
Postup:
- Přihlaste se do webové aplikace České spořitelny
- Vpravo nahoře klikněte na ikonu účtu → Nastavení
- V nabídce zvolte Informační zprávy
- V části Příchozí platby klikněte na Aktivovat
- Vyberte účet, ke kterému se má avízo týkat
- Vyberte e-mailovou adresu, na kterou se mají avíza zasílat, a doplňte ji
- Uložte změny
Avíza pak chodí z adresy ceskasporitelna@csas.cz s předmětem Přišla platba a v těle mají vypsaný variabilní symbol (VS), specifický symbol (SS) a částku — systém je rozpoznává automaticky (parser csas). Systém podporuje přeposlané avíza.
Nastavení¶
Konfigurační klíče (záložka Integrace → karta Bankovní e-mail):
| Klíč | Význam | Výchozí |
|---|---|---|
INCOMING_PAYMENT_NOTIFICATION_EMAIL_ADDRESS |
E-mailová adresa schránky | — |
INCOMING_PAYMENT_NOTIFICATION_EMAIL_PASSWORD |
Heslo / aplikační heslo k IMAPu | — |
INCOMING_PAYMENT_NOTIFICATION_IMAP_HOST |
IMAP server (např. imap.seznam.cz) |
— |
INCOMING_PAYMENT_NOTIFICATION_IMAP_PORT |
IMAP port | 993 |
INCOMING_PAYMENT_NOTIFICATION_IMAP_USE_TLS |
Použít TLS (Implicit TLS) | true |
INCOMING_PAYMENT_NOTIFICATION_IMAP_FOLDER |
Složka s avízy | INBOX |
INCOMING_PAYMENT_NOTIFICATION_REGEX_PROVIDERS |
Vlastní regex providery (JSON) | — |
PAYMENT_CHECK_INTERVAL_MINUTES |
Interval automatické kontroly — 0 = výchozí (každých 11 minut, */11), 1–59 = vlastní interval v minutách (*/N) |
0 |
Postup:
- Vyplňte host, port, TLS, adresu a heslo
- Klikněte Otestovat připojení — ověří se TLS spojení i přihlášení
- Klikněte Procházet složky — vyberte přesnou složku ze serveru
- Klikněte Uložit
- Klikněte Skenovat nyní pro okamžitou kontrolu
- Automatická kontrola pak běží dle
PAYMENT_CHECK_INTERVAL_MINUTES
Jak skenování funguje¶
- Systém se připojí přes IMAP a najde e-maily, které ještě nebyly zpracovány
- První sken projede celou schránku — najde se tak i stará avíza, která přišla ještě před nastavením integrace
- Další skeny jsou inkrementální — systém si pamatuje pozici (UID kurzor + UIDVALIDITY) poslední zpracované zprávy a žádá server jen o novější e-maily. Schránka s tisíci starých zpráv se nikdy neprohledává celá znovu
- Rozpozná banku podle odesílatele; když odesílatele nezná (např. přeposlané avízo), zkouší rozpoznat banku podle těla e-mailu
- Z každého avíza extrahuje variabilní symbol, specifický symbol, částku a datum platby
- Transakce se předá párovacímu enginu (stejný jako u API providerů)
- Zpracované e-maily se pamatují podle
Message-ID(a fallback hashe) v kolekcibank_email_messages, takže se stejné avízo nikdy nezpracuje dvakrát. E-maily se ze schránky nemazají — deduplikace probíhá v systému - Výsledek skenu se zobrazí v UI: kolik zpráv se naskenovalo, kolik bylo nových transakcí, kolik už bylo známých, kolik se nepodařilo rozpoznat (unmatched) a kolik selhalo při parsování (parse_failed)
- V kartě Bankovní e-mail a v hlavičce karty Bankovní transakce je
navíc trvalý souhrn: počet uložených e-mailů, pozice UID kurzoru a čas
poslední úspěšné kontroly (
PAYMENT_LAST_RUN_TIMESTAMP; „dosud neproběhla“, pokud kontrola ještě nikdy neproběhla)
Jak funguje UID kurzor
Pozice skenu je uložena v app_config pod klíčem
BANK_EMAIL_IMAP_CURSOR (systém používá jednu schránku, takže je klíč
pevný). Kurzor se posouvá jen přes zprávy, které se podařilo
uložit — pokud zpracování zprávy selže (dočasná chyba), příští sken se k
ní vrátí. Když se změní UIDVALIDITY schránky (např. byla znovu
vytvořena), kurzor se zahodí a schránka se projede znovu od začátku.
Reset kurzoru: pokud jste smazali záznam zprávy a chcete, aby
se e-mail zpracoval znovu, použijte tlačítko Reset kurzoru (nebo
endpoint POST /api/v1/admin/bank/email/reset-cursor). Kurzor se smaže a
příští sken projede celou schránku — dosud zpracované zprávy přeskočí
deduplikace, takže se znovu načtou jen smazané/chybějící e-maily.
Server bez podpory datového vyhledávání
Některé IMAP servery (např. „mtd”) odmítají rozsahové vyhledávání UID. Systém to automaticky pozná a přepne se na vyhledání všech UID s filtrací podle kurzoru přímo u klienta. Výsledek je stejný, jen může být o něco pomalejší u velkých schránek.
Stavy zpráv (interní)¶
Rozlišení mezi rozpoznaným avízem a „nepořádkem" se stále ukládá interně:
| Stav | Význam |
|---|---|
processed |
E-mail se podařilo rozpoznat a rozparsovat na platební záznam (VS/SS/částka) a předal se párovacímu enginu. Neznamená, že se už spároval s platbou — výsledek párování se promítá do příslušné entity (dobíjení status=completed, atd.) |
unmatched |
Žádný parser e-mail nepoznal (neznámý odesílatel ani tělo) |
parse_failed |
Parser banku poznal, ale pole se nepodařilo rozparsovat — příčina je v error_message |
Kde se to zobrazuje
Dřívější tabulka „Zprávy" v administraci byla v UI nahrazena jednotnou
tabulkou Bankovní transakce (viz výše), která pokrývá všechny
providery včetně e-mailových avíz. Kolekce bank_email_messages zůstává
interně beze změny — je to deduplikace / kurzor pro IMAP skeny a ukládá
stavy parse_failed/unmatched, které nejsou platební transakce.
Skenování avíz (nastavení, reset kurzoru, parsery) je dál pod
kartou Bankovní e-mail.
Řešení problémů¶
| Problém | Řešení |
|---|---|
| „IMAP dial … x509: certificate signed by unknown authority” | Schránka má neplatný/neznámý certifikát — ověřte hostname a certifikát |
| „Authentication failed” | Špatné heslo, nebo schránka vyžaduje aplikační heslo |
| „Mailbox … does not exist” | Špatný název složky — použijte Procházet složky pro přesné názvy |
Avízo se nespáruje (stav unmatched) |
Zkontrolujte v e-mailu VS/částku; případně doplňte regex providera |
| Banka se nepozná | Přidejte vlastní regex providera do INCOMING_PAYMENT_NOTIFICATION_REGEX_PROVIDERS |
| Sken ukazuje „0 nových transakcí” | Zpráva už byla zpracovaná dříve (deduplikace podle Message-ID) — zkontrolujte kartu Bankovní transakce; nebo avízo nesedí VS/částkou s žádnou čekající platbou |
| Smazaná zpráva se po skenu nevrátila | Kurzor už je posunutý za ni — použijte Reset kurzoru a sken znovu |
Testování celého toku v UI¶
Nejjednodušší ověření, že e-mailové avízo opravdu dobije kredit:
- Přihlaste se jako běžný uživatel → stránka Dobití → vyberte částku (např. 100 Kč) → vytvořte dobíjení
- Poznamenejte si Variabilní symbol a Specifický symbol, které systém pro dobíjení vygeneroval (zobrazí se v detailu dobíjení spolu s QR)
- Pošlete na schránku testovací e-mail — buď přes skutečný bankovní převod, nebo prostě odešlete e-mail s tělem avíza s těmito přesnými VS/SS a částkou na adresu, kterou systém sleduje přes IMAP
- Administrace → Integrace → Bankovní e-mail → Skenovat nyní
- Ověřte v odpovědi skenu, že se transakce načetla (VS/SS/částka sedí)
- Uživatel → Dobití → dobíjení je Dokončeno a kredit se navýšil
Pro rychlé otestování parseru bez IMAP je k dispozici endpoint
POST /api/v1/admin/bank/parser/test (tělo: sender, subject, date,
text) — vrátí, který parser se chytil a co z e-mailu rozparsoval.
Ukázkové e-maily (HTML/base64)
E-maily, které banky posílají jako HTML (např. Komerční banka — tělo je
base64 ve windows-1250), nelze do testu parseru vložit jako prostý text.
K tomu slouží uložené vzorky (internal/bankemail/samples/): skutečná
struktura bankovního avíza se sanitizovanými hodnotami. V UI
(Nastavení → Bankovní integrace → E-mail → Test parseru → Ukázkový
e-mail) je vyberete a spustíte test — endpoint je dekóduje přesně stejnou
cestou jako IMAP fetch (přenosové kódování + znaková sada + odstranění
HTML). Vzorky zároveň slouží jako regresní test „banka změnila formát
e-mailu" (TestSamplesParseThroughPipeline v internal/bankemail/).
Přehled všech parserů (routingová kritéria + vzory polí) vrací
GET /api/v1/admin/bank/email/parsers — stejná data, která se zobrazují
v administraci v „Přehled parserů".
Vlastní regex providery (BANK_EMAIL_REGEX_PROVIDERS)¶
Pokud banka nemá systémový parser (Go kód v internal/bankemail/), lze ji
přidat bez změny kódu — definováním vlastního regex providera v JSON.
Vlastní regex providery mají přednost před systémovými parsery: sken nejdřív
projde je (v pořadí v poli) a teprve když žádný nechytí, zkusí systémové parsery.
Kdy použít¶
| Situace | Použít systémový parser? | Použít regex providera? |
|---|---|---|
Banka je v seznamu systémových parserů (csob, csob_ceb, fio, unicredit, raiffeisenbank, ...) |
✔ | — (regex má přednost, viz řádek níže) |
| Banka nemá systémový parser (mBank, AirBank, KB, lokální banka, ...) | — | ✔ |
| Banka změnila formát avíza (systémový parser už nesedí) | — | ✔ — vlastní regex provider se sender_whitelist přebije systémový parser a opraví to bez čekání na zásah v kódu |
| Chcete vyzkoušet regex, než se napíše systémový parser | — | ✔ + „Parser test" |
| Chcete párovat akce (platby za využití hřiště/session) | ✔ | ✔ — vyžaduje VS i SS (VS = datum, SS = čas začátku akce) + částku |
| Chcete párovat dobíjení/produkty | ✔ | ✔ — vyžaduje VS + SS + částku |
Struktura JSON¶
JSON je pole objektů Provider. Povinné je jen code; ostatní pole mají
smysl jen v kombinaci s regex parserem (parser_type: "regex"):
| Pole | Typ | Povinné | Význam |
|---|---|---|---|
code |
string | ✔ | Unikátní kód providera — píše se do bank_email_messages.provider_code. Malá písmena, bez mezer (např. mbank) |
name |
string | — | Popisný název (jen pro přehled, např. „mBank") |
parser_type |
string | — | Typ parseru. Podporovaná hodnota: regex (výchozí, když chybí). Jakákoli jiná hodnota = provider se přeskočí |
enabled |
bool | — | true (výchozí chování) = aktivní; false = přeskočí se |
sender_whitelist |
string | — | Přesná adresa/adresy odesílatele, ze kterých avíza chodí. Podporuje více adres oddělených mezerou, čárkou nebo středníkem — např. notification@csob.cz, avizo@csob.cz (jedna banka může posílat avíza z více adres). Prázdné = povoleno vše (i přeposlané). Regex provider routuje podle přesné adresy, ne podle domény |
subject_pattern |
string | — | Regex pro předmět e-mailu. Prázdný = nekontroluje se |
body_pattern |
string | — | Regex pro tělo e-mailu. Prázdný = nekontroluje se |
field_patterns |
objekt | — | Mapování polí na regex extrakci (viz níže) |
system |
bool | — | Jen pro systémové parsery — u vlastních se ignoruje |
provider_ref |
string | — | Referenční identifikátor (jen pro přehled) |
Všechny regex vzory jsou case-insensitive a odolné vůči diakritice
(zkouší se originál i ASCII varianta bez diakritiky). Adresa odesílatele v
sender_whitelist se porovnává přesně (case-insensitive), včetně podpory
přeposlaných avíz.
Pole v field_patterns¶
Každý klíč = název pole, hodnota = regex s pojmenovanou skupinou
(?P<value>...) (povinná). Nepovinné pole, jehož vzor nesedí, se prostě
přeskočí — povinné je 5 klíčů, bez nichž Parse vrátí chybu:
variable_symbol, amount, currency, posted_at, recipient_account.
| Klíč | Povinné | Význam | Poznámka k vzoru |
|---|---|---|---|
variable_symbol |
✔ | Variabilní symbol (VS) | Číslice; vedoucí nuly a nečíselné znaky se ořežou |
specific_symbol |
✔* | Specifický symbol (SS) | Kritické pro všechna párování plateb. Pro akce: VS = datum (YYYYMMDD) a SS = čas (HHMMSS) začátku akce z QR; pro dobíjení/produkty: SS = hash uživatele. Párování vždy srovnává VS i SS i částku — prázdné SS nikdy neodpovídá, takže bez něj se nespáruje žádná platba |
amount |
✔ | Částka | Číslo s desetinnou čárkou (případně mezerami); znaménko určí směr |
currency |
✔ | Měna (CZK, EUR, ...) | |
posted_at |
✔ | Datum platby | Formát d.m.YYYY [H:MM]; pokud chybí, použije se datum doručení e-mailu |
recipient_account |
✔ | Účet, na který platba přišla | Číslice/pomlčka/„/" |
constant_symbol |
— | Konstantní symbol (KS) | Pro párování se nepoužívá |
counterparty_account |
— | Účet protistrany | Rozparsuje se na číslo účtu + kód banky |
counterparty_name |
— | Název protistrany | |
message |
— | Zpráva pro příjemce | |
bank_ref |
— | Referenční číslo platby (externí ID transakce) | |
balance |
— | Zůstatek po transakci | |
direction |
— | Směr platby (příchozí/odchozí apod.) |
Pokud vzor obsahuje slovo „odchozí/výdej/odepsán/outgoing...", částka se převede na zápornou |
* specific_symbol není v seznamu povinných klíčů pro Parse — ale pro
párování plateb je praktická nutné ho definovat: akce (VS = datum, SS =
čas začátku akce), dobíjení (VS+SS+částka) i produkty (VS+SS+částka)
se párují pouze, když sedí VS i SS i částka.
Všech 10 systémových parserů má vzor specific_symbol vždy definovaný (viz
přehled parserů v UI). U bank, jejichž avízo SS neuvádí, jde o „záložní" vzor,
který na reálném e-mailu prostě nematchuje → SS zůstává prázdné a parser není
rozbitý. To je záměr: bez SS se žádná platba nespáruje, takže při výpadku
párování je první kontrola, jestli formát banky SS skutečně posílá.
recipient_account je povinný jako součást formátu avíza (Parse ho od
konfigurovaného providera vyžaduje), ale jeho hodnota se nikdy neporovnává
s účtem instalace — párování plateb pracuje čistě s VS + SS + částkou.
Příklad: ČSOB CEB jako vlastní regex provider¶
Avízo „CEB Info: Zaúčtování platby" (formát podle skutečného e-mailu, údaje jsou smyšlené):
Dne 6.8.2026 byla na účtu 987654321 zaúčtována transakce typu: Příchozí úhrada okamžitá.
Účet: 987654321, CZK, OBEC DOBROVICE
Částka: +250,00 CZK
Účet protistrany: 670100-1234567890/6210
Název protistrany: JAN NOVÁK
Variabilní symbol: 1234567890
Konstantní symbol: 1234
Specifický symbol: 1234567890
Zpráva příjemci: Platba test
Zůstatek na účtu po zaúčtování transakce: +125 000,00 CZK.
Odpovídající JSON do BANK_EMAIL_REGEX_PROVIDERS:
[{
"code": "csob_ceb_regex",
"name": "ČSOB CEB (regex)",
"parser_type": "regex",
"enabled": true,
"sender_whitelist": "notification@csob.cz",
"subject_pattern": "CEB\\s+Info\\s*:\\s*Za[úu]čtov[áa]n[íi]\\s+platby",
"body_pattern": "Za[úu]čtov[áa]n[íi]\\s+transakce",
"field_patterns": {
"variable_symbol": "(?i)Variabilní\\s+symbol\\s*:\\s*(?P<value>[0-9]+)",
"specific_symbol": "(?i)Specifický\\s+symbol\\s*:\\s*(?P<value>[0-9]+)",
"constant_symbol": "(?i)Konstantní\\s+symbol\\s*:\\s*(?P<value>[0-9]+)",
"amount": "(?i)Částka\\s*:\\s*(?P<value>[+\\-]?[0-9 ]+,[0-9]{2})",
"currency": "(?i)Částka\\s*:\\s*[+\\-]?[0-9 ]+,[0-9]{2}\\s*(?P<value>[A-Z]{3})",
"posted_at": "(?i)Dne\\s*(?P<value>\\d{1,2}\\.\\d{1,2}\\.\\d{4})",
"recipient_account": "(?i)Účet\\s*:\\s*(?P<value>[0-9\\-]+)",
"counterparty_account": "(?i)Účet\\s+protistrany\\s*:\\s*(?P<value>[0-9\\-]+\\/[0-9]{4})",
"counterparty_name": "(?i)Název\\s+protistrany\\s*:\\s*(?P<value>[^\\r\\n]+)",
"message": "(?i)Zpráva\\s+příjemci\\s*:\\s*(?P<value>[^\\r\\n]+)",
"balance": "(?i)Zůstatek\\s+na\\s+účtu\\s+po\\s+zaúčtování\\s+transakce\\s*:\\s*(?P<value>[+\\-]?[0-9][0-9 .]*,[0-9]{2})",
"direction": "(?i)transakce\\s+typu\\s*:\\s*(?P<value>Příchozí|Odchozí|Výdej|Výdaj)"
}
}]
Poznámky k příkladu:
sender_whitelist: "notification@csob.cz"— regex provider routuje podle přesné adresy odesílatele (na rozdíl od systémových parserů, které kontrolují doménu). Uvádí se úplná adresa; prázdná hodnota povolí vše. Spoofing falší neprojde, protože adresa se porovnává celá- Více adres odesílatele: pokud banka posílá avíza z více adres, stačí je
uvést do
sender_whitelistoddělené mezerou, čárkou nebo středníkem — např."sender_whitelist": "notification@csob.cz, avizo@csob.cz". Obě adresy jsou pak povolené (avíza z jakékoli jiné adresy se nezpracují) - Vzory s
(?i)jsou case-insensitive; diakritika se toleruje (zkouší se i varianta bez diakritiky, takžeZaúčtováníiZauctovanichytí) amountzachytí+0,01i záporné částky;directionpodle typu transakce určí, jestli jde o příchozí/odchozí platbu — odchozí dostane zápornou částku (jako systémový parser)specific_symbolje definovaný → párují se akce (VS = datum, SS = čas začátku akce), dobíjení i produkty (VS+SS+částka). Bez SS by se nespárovala žádná platba- Ověřte si vzory v UI Parser test (tlačítko „Parser test" v kartě Bankovní e-mail) před uložením — vyplníte odesílatele, předmět a tělo a systém ukáže, co rozparsoval
Přidání nového e-mailového parseru banky (postup)¶
Každá banka s e-mailovými avízy má vlastní parser (např. csob,
csob_ceb, fio, unicredit, raiffeisenbank). Parser se sám zaregistruje
přes init() — stačí jeden nový soubor, žádné změny stávajícího kódu.
Postup (příklad pro novou banku):
- Vytvořte soubor
internal/bankemail/banks_<banka>.go - Definujte parser — struktura s metodami:
Key()— unikátní kód provideru (např.kb), píše se dobank_email_messages.provider_codeDefaultProvider()— odesílatel, subject/body vzor (jen pro UI přehled)Supports(m Message, p Provider) bool— rozpoznání e-mailu:- nejdřív
senderMatchesDomain(m, "kb.cz")(chrání před spoofingem —csob.cz.evil.comneprojde) - pak kontrola subjectu a těla (bez diakritiky + s diakritikou)
- nejdřív
Parse(m Message, p Provider) (ParsedPayment, error)— extrakce polí:- VS →
VariableSymbol, SS →SpecificSymbol, KS →ConstantSymbol(každé volitelné,required()jen pro kritická pole) - částka + měna, datum (
parseDate), účty (splitAccount), zůstatek
- VS →
func init() { registerSystemParser(BankaParser{}) }- Napište unit test
internal/bankemail/<banka>_test.gos přesnou kopií reálného e-mailu (odesílatel, subject, tělo) + spoofing test - Napište integrační test — vzor:
TestIntegration_CsobCebEmailFullFlowvinternal/app/bankemail_integration_test.go: seedTopupWithVSSS— pending dobíjení s VS/SS/částkou z e-mailubankemail.Messages tělem avíza →ep.FetchTransactionsa.TopupService.MatchBankTransactions(ctx, txs)→ dobíjení dokončeno, kredit připsán- Spusťte testy a build:
make all(+go test -tags integration ./internal/app/)
E2E testy s reálnými payloady bank API¶
Pro provider API (Fio REST / Erste PSD2) jsou uloženy přesné kopie reálných
odpovědí v internal/payment/providers/testdata/
(fio_transactions.json, erste_accounts.json, erste_transactions.json),
automaticky zabudované do integration buildů přes //go:build integration
(fixtures_loader.go). Slouží jako pojistka proti tiché změně formátu API.
Vzory testů v internal/app/payment_providers_e2e_integration_test.go:
TestIntegration_E2E_FioRealtimePayload_Parse/TestIntegration_E2E_ErsteRealtimePayload_Parse— reálný payload projde provider parserem beze změny (VS/SS/částka, external ID, provider name, stránkování u Erste)TestIntegration_E2E_FioRealtimePayload_SessionPaid/TestIntegration_E2E_ErsteRealtimePayload_SessionPaid— reálná struktura payloadu přes reálný provider →CheckPayments→ session zaplacená +bank_transactionssmatchedstavem
Při nahrávání reálné odpovědi od banky zachovejte konvenci vzorových emailů (viz výše): čísla účtů/IBAN/BIC/ID a VS/SS posuňte o konstantu (např. +7), jména nahraďte fiktivními. Hodnoty VS/SS/částky v E2E testech se přepisují z reálné struktury na hodnoty seedované session — testuje se, že struktura payloadu projde celým řetězcem a věrně se naparsuje.
SS = specifický symbol
Pro párování dobíjení je kritický specifický symbol (SS) — systém ho
generuje pro každé dobíjení z ID uživatele. Parser musí SS parsovat do
pole SpecificSymbol (ne do ConstantSymbol), jinak se transakce
nespáruje. Transaction.SS se bere pouze z SpecificSymbol — KS se
jako fallback nepoužívá. Chybějící symboly se parsují jako prázdné,
avízo bez nich se nerozbije.