Installation guide
Otkucaj · version 1.3.6 · otkucaj.com · User manual · Configuration guide
Everything this document says about the regulations is our understanding, not legal or tax advice; check with your accountant or the Tax Administration before you act.
Contents
- 1.What is installed, and what is not
- 2.Ways of delivery
- 3.Variant A: running over the internet
- 4.Variant B: a local installation on the premises
- 5.Upgrading a version
- 6.Backup and restore
- 7.Uninstalling
- 8.Checklist after installation
1.What is installed, and what is not
An electronic fiscal device consists of three elements: the ESIR, the PFR and the security element. This guide installs only the first of them. Otkucaj is an ESIR: it collects lines, discounts, payments and buyer details, assembles the request, sends it to the PFR, then displays and prints the receipt the PFR returns. It does not fiscalize receipts, does not assign the PFR number or the counter, and does not sign the receipt.
An L-PFR is bought and installed separately. A local fiscal invoice processor is a separate program or device, with its own approval from the Tax Administration and its own registration number. Its own supplier installs it, following their own instructions, either before or after Otkucaj, it makes no difference. Otkucaj only connects to it over the network, which is the procedure in chapter 4.7 of this guide. If the taxpayer does not have an L-PFR yet, the installation of Otkucaj is carried through to the end and left in demo mode, and the connection is set up when the L-PFR arrives.
The security element is issued to the taxpayer by the Tax Administration and is not installed by this guide. In smart card form it sits in the L-PFR's reader and the L-PFR itself signs with it; Otkucaj does not access the card. In file form (.pfx or .p12) it is used with a V-PFR and is installed through Settings, which the Configuration guide describes. No security element is ever copied into the program's directories by hand.
2.Ways of delivery
Otkucaj is delivered in three ways. The program is the same in all three, the database and the settings are the same in all three, and they differ only in which computer the program runs on and who sets it up. A taxpayer can move from one to another at any time, because the whole installation comes down to a database and one configuration file.
| Way of delivery | Who installs | Where the program runs | Chapter |
|---|---|---|---|
| Over the internet (cloud service) | The supplier; the taxpayer only opens an account | On the publisher's server, opened at otkucaj.com | 3 |
| Directly on the business premises | The supplier or the taxpayer's IT contractor, on site | On a computer or server at the taxpayer's premises | 4 |
| Self-installation | The taxpayer, following this guide | Opens a cloud account themselves, or locally per chapter 4 | 3 and 4 |
The way of delivery is tied to the choice of PFR. With the Tax Administration's V-PFR both ways work, because a permanent connection is a condition of working anyway. With an L-PFR only a local installation works, because an L-PFR talks to Otkucaj over the local network of the premises, and a server in the cloud cannot enter that network. As we understand art. 6 para. 4 of the Law on Fiscalization, a taxpayer who uses a V-PFR must also have an L-PFR in every business premises that has a JID, unless their retail sales are made exclusively online or are sales of their own used movable assets; Otkucaj works with an L-PFR only as a local installation.
3.Variant A: running over the internet
In this variant nothing is installed on a server: the program is already running at otkucaj.com. The installation comes down to opening an account, installing the application on the cashier's device, and checking the printing.
3.1 Opening and approving an account
Open https://otkucaj.com/registracija and fill in the business name, TIN, registration number (matični broj), phone number, e-mail, username and a password of at least 10 characters. The TIN (9 digits) and the registration number (8 digits) are checked by their control digit, and the phone must be a Serbian number, mobile or landline. At most 5 registrations an hour are accepted from one network address.
The account is created inactive and cannot be signed in with until an administrator approves it. That is deliberate: a registration has to pass through somebody who knows the taxpayer is real. If notifications are configured, the administrator hears about a new registration immediately. Once activated, the user signs in as usual, with their username and password.
3.2 First sign-in and the first-run guide
Open https://otkucaj.com/prijava and sign in. The form is protected by an invisible check (Cloudflare Turnstile), which asks for no puzzles and no characters to retype; if the checking service is unavailable, signing in goes through without it. After 8 wrong attempts in a minute, signing in from that network address is locked temporarily.
Stay signed in on this device is ticked by default and should be left on for the till in the shop: the cashier signs in once and stays signed in, even after the computer is switched off. Turn it off on someone else's or a shared computer. Signing in on every device can be ended at once under Settings, Users, with the Sign me out everywhere button; changing a password or deactivating an account does the same.
On the first sign-in a short five-step guide opens: the basics of the till, tabs for open receipts, receipt types and payments, issuing and printing, and finally installing the application. The guide can be reopened at any time: Settings, Taxpayer details, About, the Guide button.
3.3 Installing the application on a device (PWA)
Otkucaj is an installable web application: it gets its own icon, opens in its own window and keeps the item catalogue locally, so the till opens even when the connection drops. Installing is not mandatory, but it is recommended at the till, for working without internet and for the quicker shortcut.
| Device | What to do |
|---|---|
| Windows, Chrome or Edge | Open otkucaj.com, click the "Install" icon in the address bar, or use the browser menu and "Install app" (in Edge: Apps, then "Install this site as an app"). The application gets a shortcut in the Start menu. |
| Android | Open the site in Chrome, use the three-dot menu, then "Install app" (or "Add to Home screen"). |
| iPhone and iPad | Open the site in Safari, tap Share, then "Add to Home Screen". |
The same Install app button also sits in the top bar of the front page and under Settings, Taxpayer details, About. The application opens on the till screen and runs in a full window, with no address bar.
3.4 Setting up the printer and checking the QR code
- Install the printer in the operating system with the manufacturer's driver and set the paper width: a 58 mm or 80 mm roll. An ordinary A4 printer works too.
- Under Settings, Printing and payment, choose the same paper width and the receipt width in characters: usually 40 for an 80 mm roll and 32 for a 58 mm roll. The allowed range is 32 to 64 characters.
- In the browser's print dialog (Ctrl+P) set margins to None, scale to 100%, and turn headers and footers off. The browser remembers these, so they are done once per device.
- Print a test receipt and measure the QR code with a ruler. It must be a square with a side of 40 to 50 mm. Otkucaj prints it at 41 mm on a 58 mm roll and at 45 mm on an 80 mm roll and on A4, both inside the prescribed range.
- Check that no line of the journal runs off the right edge of the paper. The printed receipt column is 46 mm on a 58 mm roll and 68 mm on an 80 mm roll, and the type size is derived from the number of characters per line, so a narrower roll is handled by lowering the character count from 40 to 32, not by lowering the print scale.
3.5 Verifying the installation
The installation is finished once one Training receipt has been through the whole flow. That type follows the same path as a real receipt (PFR, journal, QR code, printing), but carries the message that it is not a fiscal receipt, does not count as turnover and is excluded from every report, so it is safe to test with.
- Open the till, add one item and choose the receipt type Training.
- Issue the receipt and wait for the journal with its QR code to appear.
- Click Print and check the result: every line inside the paper, the QR code 40 to 50 mm, the text legible.
- Scan the QR code with a phone and open the verification address.
- Open Receipts and check that the receipt is in the register.
4.Variant B: a local installation on the premises
In this variant the whole of Otkucaj runs on a computer at the taxpayer's premises. It is the variant that working with an L-PFR and working without internet require. The procedure below goes from an empty computer to a verified receipt and is carried out once per point of sale.
4.1 System requirements
- A computer running Windows or Linux.
- PHP 8.3 or newer (the publisher runs 8.5 in production), with the pdo_mysql, mbstring, openssl and curl extensions. The json extension has been part of the core since PHP 8. Check with php -m.
- MySQL 8.0 or newer, or MariaDB 10.6 or newer.
- A web server that runs PHP over FastCGI or the like (nginx, Apache, IIS).
- An L-PFR, separately approved by the Tax Administration, installed on the same computer or the same local network, with a card reader and the security element. It is not part of Otkucaj and is bought separately (chapter 1).
- A thermal printer with a 58 or 80 mm roll and a driver for that system, or an ordinary A4 printer.
If the server is to e-mail receipts itself, it also needs a way to send mail. Without it everything else works and e-mailing receipts does not.
4.2 Unpacking and directory permissions
The delivered package holds three directories and the database schema:
otkucaj/ app/ the program, settings, translations, views public/ the only directory the web server may serve bin/ command-line tools (migrate.php, seed.php) schema.sql the database schema
Unpack the package outside the directory the web server serves, for example into /var/www/otkucaj on Linux or C:\otkucaj on Windows. The web server will later point only at the public subdirectory.
Permissions: let every file be owned by the user the web server runs as (on Linux most often www-data). Read access is enough for the program to run. Inside its own directory it writes, while running, to one path only, private, beside app and public, where it keeps the digital certificate of the security element. You do not have to create it in advance: the application creates it itself when the first certificate is installed, with permissions 0750, and writes the certificate file itself with permissions 0600.
4.3 Creating the database and importing the schema
Create the database and its user. The character set must be utf8mb4, because the interface, the item names and the whole journal are in Cyrillic:
CREATE DATABASE kasir CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'kasir'@'127.0.0.1' IDENTIFIED BY 'PASSWORD'; GRANT ALL PRIVILEGES ON kasir.* TO 'kasir'@'127.0.0.1'; FLUSH PRIVILEGES;
Then import the schema:
mysql -u kasir -p kasir < schema.sql
The schema creates the tables and writes the starting state: the default Serbian tax labels (Ђ 20%, Е 10%, Г 0%, А outside VAT), a receipt width of 40 characters, and both environments, with the test environment active and in demo mode. The taxpayer's details are deliberately left empty: this is a transferable product and a receipt must not name a company that does not exist. They are entered later, through Settings.
4.4 The configuration file
Copy app/config.sample.php to app/config.php and fill in five values. The file returns an array and looks like this:
<?php
return [
'debug' => false,
'base_url' => 'http://localhost:8080',
'app_key' => '64 hexadecimal digits',
'db' => [
'host' => '127.0.0.1',
'name' => 'kasir',
'user' => 'kasir',
'pass' => 'PASSWORD',
],
];
| Parameter | What it means and how it is set |
|---|---|
| debug | Must stay false on a working installation. With true, error messages are printed to the visitor instead of going only to the server log. |
| base_url | The address at which the application is opened, with no trailing slash. On a local installation that is a local address, for example http://localhost:8080 or the address of the computer on the network. That keeps every link inside the application on the local network. |
| app_key | The secret key of this installation, exactly 64 hexadecimal digits. It encrypts the password of the security element's certificate. Generate a new key for every installation and never carry one over from another. Generate it with php -r "echo bin2hex(random_bytes(32));". If the key is changed later, a password already saved can no longer be read and must be entered again. |
| db.host | The address of the database server, most often 127.0.0.1. |
| db.name · db.user · db.pass | The database, user and password from chapter 4.3. |
Then lock the file down: it carries the database password and the key of the installation. On Linux chmod 640 app/config.php with the web server user as owner is enough. The file sits outside the directory that is served, so it cannot be downloaded over the network; chapter 8 checks that anyway.
Once the configuration is filled in, run the schema migration. It is idempotent, every step checks the current shape first, so it is safe both on a fresh database and after every upgrade:
php bin/migrate.php
The tool prints what it applied and how many steps were already in place. Among other things it assigns the serial number of the installation, in the form OTK-XXXXXXXXXXXX. It is unique to that installation, derived from random bytes, and does not change across upgrades or after a backup is restored.
4.5 The web server, the local address and the port
The web server is configured by three rules:
- The document root is public, never the directory above it. It holds only the files that may be served.
- Every request that is not an existing file goes to public/index.php. Otkucaj has one entry program that decides for itself which page to show. No rewrite file ships with the product, so that rule is written into the server's own configuration. In nginx it is try_files $uri /index.php$is_args$args;.
- PHP runs as a FastCGI process, in the version from chapter 4.1.
The port is up to you; 8080 is usual for a local installation. The same address and port must also appear in base_url, or the addresses in e-mails and in the verification links will point at the wrong place.
The sign-in check. The protection on the sign-in form (Cloudflare Turnstile) needs internet. On an installation that works without a connection it is turned off by a write to the settings table:
UPDATE settings SET v = '0' WHERE k = 'turnstile_enforce';
Signing in still asks for a username and a password and is still locked after 8 wrong attempts in a minute. If the check is left on and there is no network, signing in goes through anyway, because the check deliberately lets the user past when the checking service is unreachable; on a local installation it is still better to turn it off explicitly.
4.6 The first administrator account
A freshly installed database has no users at all, and the registration form creates an account that is inactive until somebody approves it. The first account is therefore created from the command line:
php bin/seed.php admin PASSWORD-OF-AT-LEAST-10 "First Last"
The tool creates an administrator account, and if a user of that name already exists it sets a new password and gives them the administrator role back. The third parameter is the name that is displayed and printed on the receipt; if it is omitted, "Администратор" is written. If the item catalogue is empty, the tool also writes eight sample items, so the till is not empty at the first test; they are deleted or replaced later by the taxpayer's own catalogue.
Sign in with that account and change the password immediately under Settings, Users. The administrator opens the other cashiers through the same screen, with the Cashier role.
4.7 Connecting to the L-PFR
The L-PFR must already be installed and running, with the security element in its reader. Its supplier gives the address and port it listens on.
- Open Settings, the Environment and PFR section, for the environment you are working in.
- For Mode of operation choose L-PFR.
- In the L-PFR address field enter the address the supplier gave you, for example http://localhost:8888, or the address of the computer on the local network if the L-PFR runs elsewhere.
- Save and look at the connection status on the same section. It is read live, every time Settings and the home page are opened.
The connection to an L-PFR runs over plain HTTP on the local network, as the Technical Guide prescribes, and no certificate is sent on it: it is the L-PFR itself that authenticates against the security element, through the card reader. If the status says the PFR is unreachable, check that the L-PFR is running, that it is on the same network and that the address and port are right. If it says the card is not inserted or is asking for a PIN, that is for the L-PFR to solve, not Otkucaj.
If the Tax Administration's V-PFR is used instead of an L-PFR, the mode is set to V-PFR and the address must start with https://: plain HTTP is refused when saving, because the security element is not sent over it. The certificate and the PAC are entered following the Configuration guide.
4.8 Verifying the installation
- Open /zdravlje in a browser. The answer must be {"ok":true,"app":"kasir","version":"1.3.6"}. That means both the program and the database are working.
- Sign in with the account from chapter 4.6.
- Open Settings and check that Environment and PFR shows the expected mode and a live connection status.
- Issue one Training receipt and print it, exactly as in chapter 3.5, measuring the QR code included.
- Try to open the application's address followed by /app/config.php in a browser, then the same with /private/vpfr-sandbox.p12. Both must return an error, not content: those files sit outside the directory the web server serves.
5.Upgrading a version
An upgrade changes the program only. The app, public and bin directories from the new package are written over the existing installation, then php bin/migrate.php is run. Take a backup of the database first, per chapter 6.
What an upgrade does not touch: app/config.php (it is not in the package and is never overwritten), the private directory with the security element, the whole database with its receipts, items, users and settings, and the serial number of the installation, which stays the same across upgrades and after a restore, because it is held in the database. If the application is installed on the cashier's device, the new version is picked up by itself the next time it is opened.
6.Backup and restore
The whole state of an installation is three things: the database, the file app/config.php and the private directory. The program can always be unpacked from the package again, so it is not archived. A copy of the database is taken with the usual tool:
mysqldump -u kasir -p kasir > kasir-2026-08-21.sql
Take one at least daily and keep it off that computer. Restoring runs in reverse: unpack the program, restore app/config.php and private, import the database copy and run php bin/migrate.php. Because the app_key from the configuration is what opens the certificate password, a database copy without that file is not enough: keep both.
7.Uninstalling
The application on a cashier's device. On Windows: Settings, Apps, find Otkucaj, then Uninstall, or open chrome://apps in Chrome and remove the icon. On Android: long-press the icon, then Remove. On iPhone and iPad: long-press the icon, then Remove App. That removes only the shortcut and the local copy; the data stays in the database on the server.
A local installation. Stop the web server, delete the program directory, and drop the database and its user. Before dropping the database, take a copy per chapter 6 without fail: every receipt ever issued is in it, and a fiscal receipt must not disappear merely because a program was removed. Check explicitly that you have also deleted the private directory, which holds the security element.
8.Checklist after installation
The installation counts as finished once every row below is confirmed. Rows 5 to 8 apply to a local installation only.
| # | Check | Confirmed |
|---|---|---|
| 1 | Signing in with a working account succeeds and the till screen opens. | |
| 2 | Settings, Environment and PFR show the expected mode and a live connection status. | |
| 3 | A Training receipt has been issued and appears in the receipt register. | |
| 4 | That receipt has been printed: no line runs off the edge of the paper, the QR code measures 40 to 50 mm with a ruler and scans with a phone camera. | |
| 5 | The address /zdravlje returns ok: true and the expected version. | |
| 6 | app/config.php and the private directory are not reachable through a browser. | |
| 7 | app_key is new for this installation, 64 hexadecimal digits long, and debug is false. | |
| 8 | A first backup of the database and of app/config.php has been taken and is kept off that computer. | |
| 9 | The serial number of the installation has been written down: Settings, Taxpayer details, About. | |
| 10 | The Configuration guide has been read and its checklist is scheduled before the first real receipt. |