01 · Context and goal
Bavariya is a chain of seven stores selling draft beer and bar snacks in small towns across the Krasnodar region. Before our project, customer records, discounts and loyalty cards were tracked separately in 1C, staff kept paper notebooks, and regulars knew their discount percentage by heart.
What the client asked for: a customer app with a loyalty card and a catalog, a dashboard for each store to handle orders, and a single admin panel where the owner sees every store in one place. On top of that, loyalty cards and point balances had to sync with 1C. That part was critical — otherwise the register and the app would show different numbers.




02 · What was hard
- 1C integration without a direct API. For the client, 1C is simply the system that keeps the books. It has no HTTP service, no endpoints, and nobody was going to stand up a REST API in it. Sync had to run through file exchange — without letting discrepancies between the app and the register pile up.
- Three roles in one system. Customers see the catalog and their own card. Store staff see only their own store's orders. The owner sees every store: revenue, the consent registry, promotions. One database, different interfaces, different permissions — and zero tolerance for permission mistakes: a seller must never accidentally see another store's data.
- The legal side. The app collects personal data — phone numbers, names, purchase history. That requires consents under 152-FZ (Russia's personal data law), a public offer agreement and a separate consent for marketing messages. Every consent has to be recorded with the date, IP address and device — if a dispute ever comes up, you need proof.
- One contract, two app stores. RuStore was a must for the Russian market. The client also wanted the App Store from day one. That means different builds, different screenshot requirements and different rules for apps involving alcohol (in this case, beer and related products).
The point wasn't to clone the app of a big grocery chain like Magnit or Perekrestok. It was to build a system for a local chain where every seller knows their regulars by face.
03 · Solutions
File-based exchange with 1C
We built a dedicated exchange service inside Docker. 1C exports three files — barcodes.json (loyalty card barcodes), bonus_points.json (customer bonus points) and total_spent.json (cumulative purchase totals). Our backend picks them up on a schedule, distributes them across MongoDB collections and updates the status of each record. In the admin panel, the owner sees when the last sync ran and how many records of each type went through, and can trigger the exchange manually with one button.
Five loyalty tiers — all configurable from the admin panel
Bronze (1%, no threshold), Silver (3%, from RUB 30,000 in purchases), Gold (5%, from RUB 80,000), Platinum (7%, from RUB 150,000), Premium (10%). Card tiers, discount percentages and upgrade thresholds are changed in the admin panel — no releases, no developer involvement. Card color and cover art are set by the admin too, and the app pulls fresh data on every login.
Consent registry with IP and device
When signing in, the customer gives three separate consents: the public offer, personal data processing, and promotions and marketing messages. Each one is logged in the registry with the date and time (down to the second), IP address and device model. The owner can export the full registry to CSV to show a counterparty or Roskomnadzor (the Russian data-protection regulator) — no SQL required.
Store dashboard — its own URL and its own permissions
Each store has its own login at /operator/login. A seller can sign in only to their own dashboard, sees only their store's orders and marks them “handed over” or “declined.” The admin sees everything centrally and can open any store as an observer. Roles are checked on every API endpoint — not on the frontend, not “we just won't show it,” but with a dedicated middleware check.
Brute-force protection on login
After several failed login attempts, the account is locked for 15 minutes. Limits are counted separately per login and per IP — you can't brute-force a specific admin account, and you can't cycle through every login from a single IP. In the operators collection, passwords are stored hashed (bcrypt), not in plain text, as is often the case in systems like this.
One Flutter codebase — two app stores
The app is built in Flutter — one codebase for Android on RuStore and iOS on the App Store. We split only what is physically different: the icon and splash screen to meet each platform's requirements, push notifications (FCM on Android, APNs on iOS) and the signing setup for each store. Logic, screens and API are shared.




04 · Results
A month after launching on RuStore and the App Store, the chain had registered more than two hundred customers. Half of them have already made several purchases and moved up from the Bronze card to Silver. For the first time, the owner sees revenue for all seven stores in one table — before, that meant phoning every seller at the end of the week.
1C remains the main accounting system at the register. The app and the admin panel don't try to replace it — they sync with it and show customers the real points the register calculated. No discrepancies.
05 · What's next
The project is now in support. The main areas of growth: driving more traffic through the app (push campaigns for promotions, banners in the catalog), extending the 1C exchange to cover new discount types, and adding online payment via SBP (Russia's instant bank-transfer system) — right now customers can pay only in cash or by card in store.
The backend and admin panel are split into services in Docker Compose, MongoDB lives on a separate volume with password-protected access, and SSL renews automatically via certbot. So when the chain opens its eighth and ninth stores, adding a new location takes about five minutes and needs neither an app release nor a developer.