[08]
BANKING TRACKER
█ TIER A · ROK 2026 · STATUS: DZIAŁA · JĘZYKI: TYPESCRIPT
Integracja regulowanego API bankowego, OCR paragonów i reguły uczące się
[FIG. 1] MISJA
Integruję regulowane API bankowe (Open Banking, PSD2, Enable Banking) w trybie wyłącznie odczytu i buduję na nich produkt, który realnie prowadzi domowe finanse: transakcje i salda z banku (Pekao) płyną same, a nowe wpisy lądują w skrzynce do „podpisania” jednym tapem. Kategoryzację projektuję jako ścieżkę eskalacji człowiek → reguły → model: każda moja ręczna korekta tworzy regułę, więc AI przejmuje pracę dopiero wtedy, gdy nauczy się z moich decyzji, i zostawia mi wyjątki. Zdjęcie paragonu rozbijam OCR-em na modelu wizyjnym: zdjęcie ląduje w prywatnym Blobie, a model dzieli je na pozycje z cenami przy zamkniętej liście 13 grup produktowych. Model dobieram do zadania pod kątem kosztu, nie prestiżu, i umiem tę decyzję uzasadnić — przy tysiącach paragonów różnica w cenie wywołania decyduje o tym, czy funkcja w ogóle ma sens. Dostarczam to jako PWA instalowaną na telefonie z pominięciem sklepu, z uwierzytelnianiem własnym PIN-em i JWT (świadomie wymieniłem wpiętego już Clerka — dla aplikacji jednoosobowej własne rozwiązanie jest prostsze i bezpieczniejsze w utrzymaniu). Tokeny bankowe są szyfrowane AES-256-GCM i nigdy nie leżą plaintextem, a na wszystkim siedzi budżet, cele, subskrypcje i rozliczenia z ludźmi. Działa na produkcji, zweryfikowane end-to-end na ~478 realnych transakcjach; pod spodem Next.js 16, Drizzle + Neon Postgres, 20 tabel i 40 Server Actions.
[FIG. 2] ARCHITEKTURA
najedź na blok, by zobaczyć opis
[FIG. 3] WYZWANIA
[+][CH-01]
Dokumentacja Enable Banking kłamała trzykrotnie, a każde kłamstwo odkrywał dopiero żywy bank. Po pierwsze: API przyjmuje snake_case małymi literami (valid_until, redirect_url), gdy dokumentacja sugerowała camelCase — a odpowiedź /auth zwraca pole url, nie authorizationUri z docs, więc klient czyta defensywnie wszystkie warianty. Po drugie: przy paginacji continuation_key bank zwraca 422, jeśli nie powtórzy się TYCH SAMYCH parametrów date_from — pierwsza wersja wysyłała parametry alternatywnie i kolejne strony padały. Po trzecie: Pekao odrzuca żądanie 90 dni historii błędem 400 — rozwiązałem to adaptacyjnym schodzeniem 90/60/30/14/7 dni, aż bank zaakceptuje zakres. Wisienka regulacyjna: PSD2 pozwala limitować nieinicjowane przez klienta pobrania do 4 dziennie, co auto-sync wyczerpywał od rana — przekazanie realnego IP i User-Agenta użytkownika jako nagłówków PSU legalnie zmienia klasyfikację żądania na inicjowane przez klienta i znosi limit, a IP jest walidowane regexem, zanim poleci do banku.
[+][CH-02]
Pierwsza wersja logowania PIN-em miała „blokadę po 4 próbach” i wyglądała rozsądnie — dopóki nie przeprowadziłem na własnym kodzie formalnego audytu bezpieczeństwa (20 findingów w czterech poziomach). Audyt wykrył dwa poważne defekty naraz: licznik prób był nieatomowy — SELECT, +1 w JavaScripcie, UPDATE — więc N równoległych żądań czytało ten sam stan i zapisywało 1, co pozwalało wyścigiem obejść lockout i brute-force'ować całą przestrzeń 10 000 PIN-ów partiami; a „poprawna” trwała blokada okazała się gorsza od ataku, bo ktokolwiek wyśle 4 błędne PIN-y, zamienia aplikację w cegłę wymagającą redeployu (self-DoS). Model docelowy: inkrementacja licznika atomowo po stronie bazy, porównanie PIN-u w czasie stałym (timingSafeEqual) i rosnący backoff 5 s → 5 min zamiast blokady — atakujący czeka wykładniczo, właściciel po pomyłce chwilę. Każdy finding audytu ma id cytowane w naprawach, więc trasa finding→fix jest w pełni audytowalna.
[+][CH-03]
Automatyczna kategoryzacja od pierwszego dnia byłaby albo głupia, albo droga — zamiast tego zaprojektowałem drogę „człowiek → reguły → AI”. Najpierw podpisuję transakcje ręcznie, ale każda korekta tworzy regułę („Żabka → Codzienne”), więc znane sklepy szybko schodzą same; AI wchodzi na końcu i podpisuje według wyuczonych reguł, a do mnie trafiają tylko wyjątki. System zdobywa zaufanie stopniowo — dokładnie w tempie, w jakim na nie zapracował.
[+][CH-04]
Saldo konta kłamie: nie uwzględnia czynszu, który zejdzie za tydzień, niezapłaconych subskrypcji ani pieniędzy zaklepanych na cele. Rozdzieliłem pieniądze na trzy warstwy — konta realne (salda z banku), koperty budżetowe (kontrola wydatków) i cele/rezerwy (nietykalne) — a „naprawdę dostępne” to salda minus zobowiązania i środki celowe. Jedna liczba, której mogę zaufać przy kasie, okazała się najtrudniejszym modelem danych w całej aplikacji.
[FIG. 4] WARSTWA AI
AI wchodzi tam, gdzie boli: Claude Haiku 4.5 (vision, przez Vercel AI Gateway) czyta zdjęcia paragonów na pozycje z cenami, a kategoryzacja transakcji zmierza ścieżką „człowiek → reguły → AI” — model przejmuje podpisywanie według reguł wyuczonych z moich korekt. Wybrałem Haiku świadomie: OCR paragonu to zadanie proste i częste, więc decyduje koszt pojedynczego wywołania, nie maksymalna inteligencja modelu.
[FIG. 2A] PARAGON → POZYCJE
| MLEKO 2% | 4,99 |
| CHLEB ŻYTNI | 7,50 |
| MASŁO EXTRA | 8,99 |
| JABŁKA 1KG | 5,49 |
PARAGON CZEKA NA SKAN
- ŻABKA −23,50░
- ORLEN −180,00░
- NETFLIX −43,00░
→ każdą transakcję podpisujesz ręcznie — inbox rośnie