How to Build a POS System From Scratch
“Build a POS from scratch” is usually searched by someone who has already outgrown a packaged till, or who has a shop whose catalog, hardware, or offline rules fight every product on the market. They want a stack list. What they need is a **module order**: what must exist before a cashier can take money, and what can wait.
This is the POS cluster pillar. Budget language for a first till is the same as any operational system: [custom software development cost in 2026](/blog/custom-software-development-cost-2026). This page is the build order, not a second price list. Delivery for a custom till is [custom software](/services/custom-software); the back office is usually a [web application](/services/web-applications); the register is often a tablet — [mobile applications](/services/mobile-applications).
Do not start with a UI kit. Start with the job at the counter.
The job is a completed sale, not a dashboard
Write one sentence: *A cashier can sell, refund or void, print proof, and leave a stock movement — with or without a working WAN.* If you cannot write it, you do not have a POS. You have a merchandising idea.
A first useful till is:
Identity for staff (who opened the drawer) A catalog the till can sell from A cart that can discount and tax in a way you can defend A payment through a provider, not a homemade card vault A receipt (paper, digital, or both) that matches what was charged A printer path that fails loudly instead of silently An offline sentence you can explain to a new hire in thirty seconds
Loyalty, kitchen displays, franchise portals, and a customer-facing kiosk are later modules. [MVP vs full product](/blog/custom-software-development-cost-2026) applies: one counter that cannot lose a sale beats a half-built loyalty platform.
If a packaged till already completes that sentence, do not custom-build. Stay on the package until it is the bottleneck. [Custom vs off-the-shelf](/blog/types-of-software-companies-and-who-to-hire) is the commercial fork; this article assumes you already chose custom, or you are specifying custom so you know what you are buying.
Prerequisites before a repository
You need a decision-maker who can stand at the counter twice a week. You need a photo of the actual till: mounts, paper, drawer, scanner, customer screen. You need the payment provider you must keep, or permission to change it. You need the catalog source of truth: sheet, ERP, Shopify, someone’s head.
You do not need a 40-page specification. You need [discovery](/blog/how-rootoverzero-scopes-custom-software) honest enough that “sold” has a meaning. Our [scoping approach](/blog/how-rootoverzero-scopes-custom-software) is the same as for other operational software; the difference is hardware and a queue when the radio drops.
If the catalog still lives in Excel, read [when spreadsheets become the business system](/blog/when-spreadsheets-become-the-business-system). Replacing that file is POS plus inventory, not a prettier sheet.
Treat the POS as modules, not a monolith screen
A till that is one giant “checkout page” becomes untestable. Split the product the way the counter actually fails:
Module, Job, First-release bar. Catalog, What can be sold, at what price, with what tax, Named SKUs, variants you actually use, a way to hide an item. Cart and tender, Sell, refund, void, split tender if you need it, One happy path plus decline and cancel. Payments, Money leaves the customer, Provider SDK or certified flow — no card PAN on your servers. Receipts, Proof of what happened, Reprint, void marking, tax lines that match the law you operate under. Printers, Paper at the counter (and kitchen if you claimed it), USB or Bluetooth path with a visible failure. Offline, What happens when WAN dies, Written rules, a queue, conflict you can explain. Back office, Cashup, reports, catalog edits, Same day the till goes live, even if ugly. Stock, Decrement, reservation, transfer, Only if the till is allowed to be wrong for minutes, not days
Each module has a contract with the others. The cart must not mark paid until the payment module says so. The receipt must not print a success line for a declined card. The stock module must not decrement on a void that never captured.
Build them in that spirit even if they share a process. “Modules” here means **bounded jobs and failure modes**, not eight microservices on day one. One service with clear packages is fine. Eight chatty services before you have a receipt is not.
Step 1 — Staff identity and a shift
Someone opened the till. Someone should be able to close it. Start with a small role set: cashier, manager. Manager can void and refund; cashier cannot invent discounts beyond a policy. Three roles beat twelve imaginary personas.
A **shift** or drawer session is not bureaucracy. It is how cashup works and how you investigate a missing fifty. If you skip it, you will glue a spreadsheet to the till in week two.
Do not build a custom identity product. Use a maintained auth approach and put effort into PINs or badges that work with wet hands and a rush.
Step 2 — Catalog you can sell from
The catalog is the first real data model. SKU, name, price, tax class, optional barcode, optional modifiers. Variants (size, color) are cheap to mock and expensive to get right at the scanner.
Rules that belong in v1 only if the shop already lives by them:
Weighted items and catch weight Recipes and ingredients (hospitality) Serials and lots Bundles that explode into components
A first release can freeze “one price, one tax class, optional variant.” That is a valid cut. Pretending you have one price while staff type a different number on every sale is how you buy a second catalog.
If ecommerce is the catalog owner, the till is a channel. Do not duplicate Shopify’s product model in secret. The storefront side is [custom Shopify apps](/blog/custom-shopify-apps) and [ecommerce](/services/ecommerce); the till still needs a local cache for offline. Who wins on price changes must be written down. Stock identity is [POS inventory synchronization](/blog/how-to-build-a-pos-system-from-scratch).
Step 3 — Cart, tax, discount, then pay
Build the cart as a document with lines, not as a pile of buttons. Lines survive reprints, refunds, and audits. Buttons do not.
Tax is a jurisdiction problem. Encode the rules you actually operate under. Do not copy a US tax engine into a market that does not work that way. Discounts need a reason and a permission. Untracked “manager price” is how cashup lies.
Then payment. Card-present data belongs with a payment provider (Stripe, Square, a local acquirer — pick from **current** options in your market). Custom POS talks to their terminal SDK or their certified flow. PCI is their documentation plus your architecture, not a blog’s checklist. **Do not invent API names or fee percentages.** Read the provider’s current docs and your acquirer’s rules.
Refunds and voids are not “the sale with a minus.” They hit the provider, the receipt, and stock. Tips, if you take them, are a second allocation. Offline card taking, if offered at all, is a **provider feature with limits** — not something to improvise with a queued PAN.
If you already sell online with Stripe and books in QuickBooks, the till still has to agree with both. That glue is [buy Stripe and QuickBooks, build the workflow](/blog/buy-stripe-and-quickbooks-build-the-workflow), not a second ledger.
Step 4 — Receipts as a document, not a screenshot
A receipt is a legal-ish artifact in many places and a customer-trust artifact everywhere. Treat it as a stored document: sale id, lines, taxes, tender, timestamp, staff, reprint count.
Paper and email/SMS are delivery channels for that document. If you only generate pixels at print time, you cannot reprint after a jam, and you cannot show finance what happened.
Receipt layout (logo, footer, QR) is design work. Receipt **truth** is product work. Kitchen tickets are a different document type: they omit prices, include modifiers, and route to a different printer. Do not reuse the customer receipt template and hope.
Step 5 — Printers as a module
Printers fail more often than payment APIs in shops we actually see. USB unplugged, Bluetooth unpaired, paper out, kitchen on a different subnet, two tills fighting one device.
Conceptual integration — ESC/POS, USB, Bluetooth — belongs in [POS printer integration](/blog/how-to-build-a-pos-system-from-scratch). Android pairing and permission failures belong in [Android Bluetooth thermal printer](/blog/how-to-build-a-pos-system-from-scratch). This pillar’s rule is simpler:
Name every printer in the first release (counter, kitchen, label) Print is a job with status: queued, sent, failed, reprint Failure is visible on the cashier UI before the next customer Check **printer vendor docs** for command sets and connection modes. Do not treat a blog as a driver.
Cash drawers often open via the printer’s kick pulse. That makes the printer module also a drawer module. Test it.
Step 6 — Offline is a product
“Works offline” is not a checkbox. The comparison of operating models is [offline POS vs cloud POS](/blog/how-to-build-a-pos-system-from-scratch). In the build, you still have to pick a sentence:
UI cached, nothing sells (honest, often useless) Sell from a local item file, queue tickets, sync later Conflict when two tills sell the last unit Payments that cannot capture until online — staff must know
Each step up is real engineering and a test in a shop with bad radio. A bid that says “offline” without conflict rules is incomplete.
Implementation sketch, not a framework:
Local catalog snapshot with a version Local sale documents with client-generated ids A sync worker that uploads in order and records server ids A human path when the server rejects a line (price changed, SKU gone, stock denied)
Do not invent a CRDT if a manager override and a printed exception report would do.
Step 7 — Back office the same week as the till
Cashup, a sales report, a way to add an item. If those live only in a contractor’s laptop, you have not launched. The back office can be ugly. It cannot be missing.
A browser back office and a tablet till is a normal split. See [web app vs website](/blog/web-app-vs-website): the office is operational software, not a marketing site.
Step 8 — Stock only when the shop will feel the lie
If the till is allowed to be wrong until tonight’s count, you can ship stock as a nightly file. If two channels sell the last unit, you need reservations and [inventory synchronization](/blog/how-to-build-a-pos-system-from-scratch). Inventory cost and units of measure live in [inventory software development cost](/blog/how-to-build-a-pos-system-from-scratch). Do not hide a warehouse product inside “POS v1.”
Device surface: Android, iOS, dedicated terminal, browser
The register is a device with a kiosk personality. [Android POS development](/blog/how-to-build-a-pos-system-from-scratch) covers tablets, Play, and fleets. Dedicated terminals have vendor SDKs; browser-only tills fight printers and offline. Choose on purpose. Store policies change; **check current Play and Apple docs** if you distribute that way. Sideloading a fleet is an operations product, not a shortcut.
Hospitality extras (tables, courses, kitchen routing) and ticketing (events, queues) are different shapes. Ticketing vs retail is [POS ticketing system development](/blog/how-to-build-a-pos-system-from-scratch). Do not copy a supermarket cart into a door list.
What not to build first
A customer CRM with segments A franchise BI warehouse A custom payment switch Pixel-perfect brand for the cashier (speed beats moodboards) Multi-country tax engines “for later”
[Software that holds up](/blog/software-that-holds-up) means the Tuesday the printer jams, the card declines, and two cashiers share a SKU. Demo-only POS is unfinished.
Cutover
You cannot cut over a till on a slide. Plan a quiet hour, a spare device, dual-running, and a rollback to the old drawer. Calendar includes payment sandbox, printer soak tests, and staff shadowing. [Custom software timeline](/blog/how-rootoverzero-scopes-custom-software) plus store-specific gates if you ship via Apple or Google.
How RootoverZero sequences a till
We list devices, tender, and the offline sentence before we pick a framework. If the budget only fits a packaged till, we say so. Custom starts when the package fights the shop.
If this is your counter, [discuss custom software](/services/custom-software) or send a brief through [onboarding](/onboarding). Read [how we work](/how-we-work) if you want the delivery shape. For budget, start with [custom software development cost in 2026](/blog/custom-software-development-cost-2026).
FAQ
Do we need microservices to have modules?
No. Modules are jobs and failure modes. Split processes when a team or a load problem appears, not to look modern on a diagram.
Can we print later and skip the printer in v1?
Only if the shop can legally and operationally sell without paper. Most cannot. Treat print as in-scope or write the exception.
Should the catalog live only on the device?
A device-only catalog dies when the tablet dies. Local cache plus a server (or a store server) is the usual adult pattern. Offline vs cloud is a separate decision.
Is a browser till a real POS?
It can be, for a counter with reliable power, a supported printer path, and an honest offline story. It is a poor default when you promised Bluetooth printers and a dead-zone warehouse.