Šta se desi kada kupac skenira NBS IPS QR kod
Bankarska aplikacija iz slike čita tekst sa podacima naloga: primaoca, račun, iznos i ostale navedene elemente. Kupac proverava pripremljen nalog i potvrđuje ga u banci. Sam QR nije nalog za povlačenje novca sa računa, a fotografija koda ne govori vašoj aplikaciji da je uplata izvršena.
NBS razlikuje kod za plaćanje računa od IPS prihvatanja na prodajnom mestu. Za potonje je potreban odgovarajući odnos sa bankom prihvatiocem. Ako vam treba automatska potvrda naplate, planirajte taj ugovoreni način prihvatanja i njegove poruke. Dodavanje QR slike na PDF samo po sebi ne pravi takvu integraciju.
Od poslovnih podataka do slike: podelite implementaciju
Koristan redosled je da prvo utvrdite koju obavezu korisnik plaća, zatim sastavite njen zapis, pa tek onda generišete sliku. Generatoru QR simbola ne treba dozvoliti da sam računa dug ili bira račun primaoca. Njegov ulaz treba da bude već proveren tekst.
U aplikaciji sa PDF dokumentima čuvajte snimak podataka pri izdavanju. I pisano uputstvo i QR tada nastaju iz istog računa, iznosa i poziva na broj. Ako se konfiguracija bankovnog računa sutra promeni, ne želite stari dokument sa jednim brojem u tekstu i drugim brojem u novoj QR slici. To pravilo primenjujemo i u novom NIJEFILA portalu.
Kako izgleda tekst unutar QR koda
Podaci su parovi oznake i vrednosti, razdvojeni znakom |. Zapis počinje sa K:PR|V:01|C:1; oznaka C:1 znači UTF-8. Nema završne uspravne crte. Sledeći zapis je isključivo primer za razvoj: broj računa preuzet je iz javnog primera NBS, naziv primaoca i namena su izmišljeni. Nemojte ga koristiti za uplatu.
textK:PR|V:01|C:1|R:845000000040484987|N:PRIMER DOO|I:RSD3596,13|SF:221|S:Primer za proveru QR koda|RO:97162026000001
R: račun primaoca, 18 cifara bez crtica.N: naziv primaoca, do 70 znakova.I: oznaka RSD i iznos sa decimalnim zarezom, bez razmaka ili razdvajača hiljada.SF: šifra plaćanja. U primeru je 221; stvarnu šifru izaberite prema osnovu plaćanja.S: opciona svrha, do 35 znakova u jednom redu. Prazno opciono polje izostavite.RO: model i poziv na broj. Za model 97, u zapisu nema crtica.P: opcioni podaci o platiocu. Primer ih izostavlja; identifikaciju obaveze vodimo kroz poziv na broj.
Ograničenja i ostale dozvoljene kombinacije nalaze se u preporukama NBS. Primer ispod namerno podržava uži slučaj: pozitivan dinarski iznos, jednoredne tekstove i numerički model 97. Ne pokriva sve modele, višeredne adrese niti potvrdu da račun zaista pripada navedenom primaocu.
Račun primaoca: zašto brisanje crtica nije dovoljno
U formatu sa crticama srednji deo može biti kraći od 13 cifara. Ako samo obrišete crtice, dobićete prekratak broj. Nule se dodaju unutar srednjeg dela, uz očuvanje prve tri cifre i završne dve kontrolne cifre. Primer NBS 840-955845-10 tako postaje 840000000095584510.
U kodu prvo prihvatamo tačno definisan oblik, zatim formiramo 18 cifara i proveravamo kontrolni broj. Funkcija control97, uključena u ceo primer za preuzimanje, računa kontrolne cifre celobrojno. Neispravan unos zaustavlja generisanje, umesto da nastane slika koju bi korisnik tek kasnije pokušao da plati.
javascriptexport function account18(value) { if (typeof value !== 'string') throw new TypeError('Account must be text'); const raw = value.trim(); const parts = /^([0-9]{3})-([0-9]{1,13})-([0-9]{2})$/.exec(raw); const digits = parts ? parts[1] + parts[2].padStart(13, '0') + parts[3] : raw; if (!/^[0-9]{18}$/.test(digits) || control97(digits.slice(0, 16)) !== digits.slice(16)) { throw new RangeError('Invalid account format or control digits'); } return digits; }
Iznos čuvajte u parama i formatirajte na kraju
Primer prima 359613n, što predstavlja 3.596,13 RSD, i pravi vrednost RSD3596,13. Veliko slovo n označava JavaScript bigint. Svi računi do tog trenutka koriste cele pare, pa razdvajanje dinara i para ne uvodi dodatno decimalno zaokruživanje. MDN objašnjava BigInt i razlike u odnosu na tip Number.
javascriptexport function amountTag(minor) { if (typeof minor !== 'bigint' || minor <= 0n) throw new RangeError('Use positive bigint minor units'); const value = `RSD${minor / 100n},${String(minor % 100n).padStart(2, '0')}`; if (value.length > 18) throw new RangeError('Amount does not fit the I field'); return value; }
Ne pretvarajte proizvoljan unos korisnika prvo u Number, pa ga tek onda množite sa 100 i pretvarajte u BigInt. Time biste problem sa decimalnim prikazom premestili ispred funkcije. Iznos uzmite iz već proverenog obračuna u najmanjim jedinicama. Kada ga šaljete kroz JSON, prenosite ga kao dogovoren decimalni string i proverite taj format na prijemu; običan JSON ne serijalizuje BigInt automatski.
Model 97 i kontrolni broj nisu isti podatak
U vrednosti RO:97162026000001, prve dve cifre označavaju model 97. Sledeći deo je sam poziv na broj, uključujući njegove kontrolne cifre. Ne dodajte još jedan model ispred već kompletne vrednosti i ne zamenjujte korisnički broj dokumenta slučajnim identifikatorom porudžbine.
javascriptexport function reference97(value) { if (typeof value !== 'string' || !/^[0-9]+(?:-[0-9]+)*$/.test(value)) { throw new RangeError('Use a numeric model 97 reference'); } const digits = value.replaceAll('-', ''); if (!/^[0-9]{3,23}$/.test(digits) || control97(digits.slice(2)) !== digits.slice(0, 2)) { throw new RangeError('Invalid model 97 reference'); } return '97' + digits; }
Ovaj pomoćni kod prihvata samo numerički poziv na broj po modelu 97. Drugi modeli zahtevaju druga pravila. Model 00 postoji kao drugačiji slučaj u specifikaciji, ali ga ova funkcija namerno ne obrađuje. Vaš sistem za usaglašavanje mora da čuva isti poziv na broj koji šalje u QR zapis, zajedno sa iznosom i valutom obaveze.
Generišite SVG lokalno i dodajte ga u PDF
Ceo primer za preuzimanje sadrži funkcije, probne podatke, testove i mali program za izradu SVG-a. Potreban je Node.js sa podrškom za ES module i BigInt. U zasebnom direktorijumu sa primerom pokrenite:
bashnpm ci npm test npm run example
Program upisuje payload.txt i primer.svg lokalno. Koristi uqr 0.1.3, sa tačnom verzijom u paketu i zaključanim zavisnostima. Deo koji crta sliku izgleda ovako:
javascriptimport { renderSVG } from 'uqr'; const svg = renderSVG(text, { ecc: 'M', border: 4, whiteColor: '#ffffff', blackColor: '#000000', });
Prazan prostor oko simbola, quiet zone, pomaže čitaču da odredi njegove granice. DENSO WAVE navodi marginu od četiri modula sa svih strana. Modul je jedna osnovna ćelija QR mreže, pa četiri modula nisu nužno četiri ekranska piksela. Marginu sačuvajte i pri ubacivanju u PDF.
Uz sliku navedite oznaku NBS IPS QR, kao što traže smernice za servis. Ostavite i čitljive pisane podatke za ručni nalog. Koristite taman simbol na beloj podlozi, bez odsecanja margine, logotipa preko koda i rastezanja po jednoj osi. Proverite završni PDF, jer ispravan SVG ne dokazuje da ga štampač ili PDF alat nisu smanjili do nečitljivosti.
Proverite zapis zvaničnim NBS validatorom
NBS omogućava proveru teksta kroz metod validate. Iz direktorijuma sa već napravljenim fajlom pošaljite njegov sadržaj kao tekst. Ne dodajte novi red na kraju fajla. Komanda ispod je provera formata, bez slanja naloga banci:
bashcurl --silent --show-error --fail-with-body \ -H "Content-Type: text/plain; charset=utf-8" \ --data-binary @payload.txt \ https://nbs.rs/QRcode/api/qr/v1/validate
Pored HTTP odgovora pročitajte JSON polje s.code; vrednost 0 označava uspešnu tehničku proveru. Greške mogu biti objašnjene i u polju e. Metod generate prima tekst i vraća i sliku u Base64 obliku, dok gen prima JSON polja i vraća binarnu sliku. To su različiti zahtevi, pa im ne šaljite isti oblik tela naslepo.
U proveri od 9. novembra 2026. NBS validator je prihvatio dati primer i zapis sa slovima č, ć, ž, š i đ. Dodat završni znak | vratio je kod 620, dok su promenjene kontrolne cifre računa i poziva na broj vratile kod 608. Proverili smo i lokalno generisan SVG pretvoren u PNG: metod upload očitao je isti zapis i vratio s.code 0. To su tehničke probe sa razvojnim podacima, bez izvršene uplate.
Za ručnu proveru završne raster slike postoji i NBS validator slike. Odvojite proveru podataka od provere čitljivosti: ispravan tekst može da završi u loše odštampanom simbolu. Na probnom PDF-u proverite da bankarska aplikacija očita očekivana polja; nemojte potvrđivati plaćanje razvojnog primera.
Šta obavezno testirati pre uvođenja
- Najmanji iznos od jedne pare, cele dinare i vrednost sa obe decimale, bez upotrebe lokalnog prikaza sa tačkama za hiljade.
- Kratak i pun oblik računa, pogrešan kontrolni broj i ulaz koji je IBAN umesto domaćeg računa.
- Ispravan i neispravan poziv na broj; proverite da model nije dodat dva puta.
- Slova č, ć, ž, š i đ, prazna opciona polja, završnu uspravnu crtu i pokušaj ubacivanja novog polja kroz znak | u nazivu.
- PDF koji stvarno šaljete korisniku, njegov prikaz na drugom ekranu i štampu; sačuvajte belu marginu.
- Plaćanje manjeg ili drugačijeg iznosa, duplo obaveštenje i uplatu drugog lica u testnom poslovnom toku, bez stvarnog prenosa novca.
Validator proverava tehničku usklađenost zapisa. Ne dokazuje da je račun vaš, da je izabrana prava poslovna obaveza ili da je novac stigao. Kontrolni broj prepoznaje određene greške unosa; nije digitalni potpis koji štiti PDF od zamene. Zato podatke primaoca uzimajte iz kontrolisane konfiguracije i proverite ih pre izdavanja dokumenta.
Potvrdu priliva projektujte kao zaseban korak
U NIJEFILA sistemu QR pomaže kupcu da pripremi uplatu. Priliv se zatim proverava na izvodu banke i povezuje sa konkretnim zahtevom. Tek izmirenje tog zahteva pokreće dalje poslove prema pravilima usluge. To je zaseban tok od lokalnog generisanja slike ili pozitivnog odgovora NBS validatora.
Za svoju aplikaciju definišite kako prepoznaje bankarski priliv, ko može da ga potvrdi i kako sprečava da se ista transakcija obradi ponovo. Poziv na broj je koristan ključ za povezivanje, ali proverite i valutu, iznos i stanje obaveze. Kada potvrda nije automatski dostupna, prikažite da uplata čeka proveru i omogućite korisniku da pošalje podatke o transakciji.
Česta pitanja
- Da li mi treba poseban ugovor sa bankom da generišem NBS IPS QR za račun?
- Javni NBS generator i validator omogućavaju pripremu koda za plaćanje računa. To se razlikuje od ugovorenog IPS prihvatanja na prodajnom mestu i njegove potvrde. Proverite sa svojom bankom šta vam treba ako uvodite taj način prihvatanja naplate.
- Mogu li ceo iznos da pošaljem kao 3.596,13 RSD?
- Ne u tom obliku u polju I. Primer formira RSD3596,13, bez razmaka i bez tačke za hiljade. Iznos u aplikaciji vodi u celim parama, a prikaz za ljude formatira odvojeno.
- Zašto QR validator prihvata kod, a kupac ga ne čita iz PDF-a?
- Provera tekstualnih podataka ne potvrđuje čitljivost završne slike. Proverite marginu, kontrast, rezoluciju i veličinu u PDF-u i na štampi, pa isprobajte očitavanje bez potvrđivanja plaćanja razvojnog primera.
- Da li s.code 0 znači da je uplata izvršena?
- Ne. To je uspešna tehnička validacija zapisa. Potvrdu priliva dobijate odvojenim bankarskim ili ugovorenim prihvatnim postupkom, a ne od generatora ili validatora QR koda.
Reference
- [01]NBS: preporuke za pripremu tekstualnog zapisa NBS IPS QR
- [02]NBS: smernice za Generator/Validator API
- [03]NBS: javni Generator/Validator
- [04]NBS: plaćanje računa i razlika u odnosu na IPS prihvatanje
- [05]DENSO WAVE: margina oko QR simbola
- [06]uqr: dokumentacija za generisanje QR simbola
- [07]MDN: BigInt
Osnivač NIJEFILA DOO. Piše o hostingu, DNS-u, email infrastrukturi i serverskoj administraciji.