Benny.mk — Benefits Beyond the Office
7/11/2026

Screenshots below are from the real product running against a seeded demo dataset — every company, merchant, employee, discount and transaction shown is generated demo data.
Overview
Benny is an employee-benefits and discounts platform built for North Macedonia — "Benefits Beyond the Office. Scan. Save. Enjoy." Companies take out a single subscription and invite their team; employees use the Benny app to unlock everyday discounts (always at least 10%) at partner restaurants, cafés, bars, bakeries, dessert shops, fast-food spots and pubs. Anyone can browse partner venues as a guest — unlocking a discount is what needs a company invite. Merchant staff complete each redemption with a quick QR scan at the table, and the Benny team runs the whole marketplace from a separate admin console.
It's a complete, production-shaped product spanning five surfaces on one API, fully bilingual (English + Macedonian), with money in MKD, geography driven by a country/city catalog, and every time-based rule computed in Europe/Skopje.

The product at a glance
Benny is a monorepo of four apps talking to one backend and one database:
benny.mk Public marketing site + guest browse + company & merchant dashboards (React + Vite)
admin.benny.mk Internal operations console (React + Vite)
mk.benny.app Employee + merchant-staff mobile app, role-aware (Expo / React Native)
api REST API, JWT auth, 8-role RBAC, billing & QR engine (Express 5 + MongoDB)
Anything actionable is invite-only — there is no public sign-up. A company admin invites employees by email or a single-use 48-hour link; a merchant owner invites their managers and waiters the same way. But discovery is open: a visitor can browse as a guest on the web or in the app to see partner venues and deals, with a prompt to sign in once they want to redeem. One users collection, one login endpoint, and role-based routing decide which of the experiences a person actually sees.
The employee app
The heart of the product is the phone in the employee's pocket. After a bilingual onboarding, employees land on a home feed personalised to their company, browse and search partner venues (filtering by country, city, category and service type), open a venue to see its live discounts on a map, and — the payoff — generate a one-time discount code to show the waiter.
The home feed leads with the employee's running savings total and their company badge, then a banner carousel, "Best deals", and "New on Benny". Search offers a list/map toggle with Country → City dropdowns (auto-selecting the nearest catalog city from the device's location) plus category and service-type filters. Each venue page shows its scheduled and fixed discounts, opening the exact time windows (e.g. Happy Hour −25%, Mon–Fri 17:00–19:00), a description in both languages, and a live Google Map pin.
Every redemption lands in the employee's Activity history — original price, discount applied, amount saved, and the running total — so the value of the benefit is always visible.
The signature move — the QR discount flow
Benny's core interaction is a two-sided QR handshake designed so a customer can never fake a discount and a waiter never has to do math. All tokens are minted server-side and every discount is calculated on the server — the client only ever displays what it's told.
- The venue displays a persistent printed QR (with a short code for anyone who can't scan).
- The employee scans it — or types the code — the app shows the discount that applies right now (the employee can pre-select from a venue's active offers), and the server computes the rate from that venue's schedule: its base rate outside any window, a scheduled rate inside one, but never below 10%.
- Benny issues a temporary customer QR that lives for five minutes with a visible countdown, plus a
SFV-3T7-style manual code. - The waiter scans that customer code, enters the receipt total, and the server re-validates the offer and returns the final price. The completed transaction instantly flows into the employee's history, the merchant's analytics, the company's savings, and the admin ledger.
Because both codes are opaque server tokens and each state transition is an atomic conditional update, the flow is safe against replay, double-redemption, and client tampering.
The in-venue app — for merchant staff
The same binary is role-aware: a waiter or manager signs in and gets a stripped-down point-of-sale surface instead of the consumer feed. It opens straight to "Scan customer code", with a manual fallback, today's orders / discounts / revenue at a glance, and a running list of the day's transactions.
The company dashboard
Company admins and managers run their benefit from a web dashboard on benny.mk. The overview surfaces the numbers a people-ops or finance lead actually cares about: active employees, pending invites, the estimated monthly cost, total transactions, and total savings delivered to the team — with a usage chart and a "where your team saves the most" leaderboard.

Employees are invited by email or single-use link and can be enabled or disabled at any time — and billing follows real usage, which is the rule the whole model turns on (more below). Analytics break down adoption and savings over time.

Billing is fully self-serve to inspect: the current plan and term, a live active-employee count, the next-invoice estimate, and a history of issued invoices — each one a proper bilingual document with line items, due dates, and payment status.
The merchant dashboard
Partner venues get their own dashboard to manage everything about their presence. The overview tracks today's and this month's redemptions and revenue; discounts can be a simple fixed percentage or a scheduled window (day-of-week + time range, always ≥10%).
The QR code page renders the venue's printable code and short code (with a "print table tent" action), staff are invited and managed here, and the profile editor carries the bilingual description, business type, service tags, gallery, and a Country → City picker plus a drag-the-pin Google Map location that powers the app's map search.
Business model, catalogs & reach
Benny is a B2B subscription: a company pays a flat 199 MKD per active employee per month on the Standard plan, and its whole team gets access to every partner discount. Merchants join to drive footfall and repeat visits from a captive, benefit-holding audience — they set their own discounts and pay nothing.
The team curates the marketplace from the admin console. Partner venues are organised into the categories employees browse by — restaurants, cafés, bars, bakeries, dessert shops, fast food and pubs — each an admin-managed "business type" with a bilingual name, an image, its own search synonyms and ordering. Geography is a first-class catalog too: a country → city dataset (North Macedonia and 35 cities, each with coordinates) powers the location filters in search and the merchant/company address forms — so Benny can expand town by town without a code change.
Those categories are what employees see as browsable tiles, backed by the same warm venue imagery:
Billing & invoicing
Billing is a monthly cron plus an on-demand admin trigger, and it's more nuanced than a flat headcount charge. Companies subscribe on 1-, 6- or 12-month terms, and each completed term produces one invoice with a line item per calendar month. Crucially, seats are counted by usage: an employee who was active on any day of a month is billed for that month — so enabling and disabling people mid-cycle is fair and fully auditable. Every status change is written to an append-only event log, disabling an employee offers a "now" or "at renewal" cutoff, and an admin can switch a company's plan immediately or at the next renewal.
VAT is a platform setting, off by default. Benny isn't VAT-registered yet, so invoices are billed without VAT until an admin flips the switch — the Macedonian ДДВ rate (18%) sits ready for the day it's turned on, and each invoice snapshots the rate it was issued under.
Invoices carry sequential numbers like BEN-2026-000042, a 14-day due date, and are rendered as bilingual PDFs; they're settled offline by bank transfer and marked paid by an admin with a payment reference. The admin console runs and reconciles the whole lifecycle, including an "overdue" state.
The admin console
The Benny team operates the marketplace from admin.benny.mk. The overview is a live pulse of the platform — companies, active employees, merchants, transactions, platform-wide savings, pending/paid invoices and a revenue estimate — over a feed of the latest transactions across every venue.

From there, admins manage every company and merchant (including regenerating a venue's QR), inspect users and the full transaction ledger, run and reconcile billing, and configure plans, VAT, banners, business types and the locations catalog.
Roles, access & security
Benny runs on eight roles from a single users collection: SUPER_ADMIN and ADMIN (admin console); COMPANY_ADMIN and COMPANY_MANAGER (company dashboard); EMPLOYEE (mobile); and MERCHANT_OWNER, MERCHANT_MANAGER and MERCHANT_STAFF (merchant dashboard and/or the staff app). A single login endpoint routes each person to the right surface — and unauthenticated visitors get the read-only guest browse.
Access is deliberately tight: invite-only onboarding with single-use, 48-hour-expiring links; device-locked employee accounts (an X-Device-Id binding) to stop benefit-sharing; server-authoritative discount math; zod-validated public endpoints; and non-enumerating password resets.
Architecture & tech
- Backend — Node.js + Express 5, MongoDB/Mongoose 8, JWT auth, 8-role RBAC, zod request validation. Services for the scan state machine, discount calculation, term/usage-based billing, PDF invoices (pdfkit), analytics rollups, QR tokens and transactional email (Resend). Cron jobs generate invoices, snapshot daily billing usage, and expire invites. A
/api/public/*surface powers unauthenticated guest browse. - Web & admin — React 19 + Vite + Tailwind 4,
react-i18nextfor full EN/MK bilingual UI, Google Maps for location pickers, same-origin API via a dev proxy. - Mobile — Expo SDK 57 / React Native with expo-router, a single role-aware binary, expo-camera QR scanning, react-native-maps, expo-location (nearest-city preselect), SecureStore-held JWT, and universal/app links (
benny://, benny.mk) so an invite opens straight into the app. - Data model — Users, Companies, Merchants, Discounts, Invites, Invoices, ScanSessions, Transactions, SubscriptionPlans, CompanySubscriptions, Banners, daily analytics rollups, and the newer Settings (VAT), BusinessType, Country/City, BillingUsageDaily and EmployeeStatusEvent collections — with a seed that builds an entire realistic demo world (companies, employees, ten real Skopje venues, discounts, ~120 transactions and two months of invoices).
What made it work
Benny's difficulty was never any single screen — it was coherence across five surfaces and eight roles without drift. Three decisions carried it: one API with strict role-based routing so every client (down to the guest browser) is a thin view over the same truth; a server-authoritative QR engine so discounts can't be gamed and the two-sided scan "just works" at a busy table; and treating bilingual + MKD + Skopje-timezone as first-class from day one rather than a retrofit. The billing engine grew the same way — from a flat headcount charge into fair, auditable, usage-based invoicing with configurable terms and VAT — without the dashboards above it having to change. The result is a benefits marketplace that feels effortless to the employee, trivial to the waiter, and fully accountable to the company and the operator.
Deliverables
- Public marketing site + guest browse (benny.mk) — bilingual EN/MK landing, unauthenticated merchant discovery, FAQ, legal, deep-link invite fallback
- Company dashboard — employee invites, enable/disable, savings analytics, billing + PDF invoices
- Merchant dashboard — fixed & scheduled discounts, printable venue QR, staff, transactions, analytics, map profile
- Internal admin console — companies, merchants, users, transactions, invoicing & plans, image-backed business-type & country/city catalogs, VAT settings
- Role-aware iOS/Android app (Expo/React Native) — employee discovery + QR redemption and merchant-staff checkout
- QR discount engine — dual-token scan flow, server-side discount math (always ≥10%), Europe/Skopje scheduling
- Subscription billing engine — usage-based per-active-employee invoicing across 1/6/12-month terms, configurable VAT, cron-generated bilingual PDF invoices
- Node/Express/MongoDB API — 8-role RBAC, invite-only onboarding, device-locked anti-sharing