Build a Professional POS System

A full professional POS (like Square / Shopify POS) requires multiple modules. Here we’ll show you a production-ready architecture + working core code so you can scale it easily using your preferred stack:

  • Frontend: HTML + TailwindCSS + jQuery
  • Backend: PHP (REST style)
  • Database: MySQL
  • PWA: Offline POS support
  • Hardware support: Barcode scanner, receipt printer
  • Responsive: tablet / mobile / desktop

Typical real POS modules:

  1. Authentication (Cashier login)
  2. Products + Inventory
  3. Barcode scanner
  4. Cart
  5. Orders
  6. Payment (cash/card/mobile)
  7. Receipt printing
  8. Sales reports
  9. Customers
  10. Offline sync (PWA)

Below is a complete base structure you can deploy and expand.


1. Project Structure

pos-system/
│
├── index.php
├── login.php
├── dashboard.php
│
├── manifest.json
├── sw.js
│
├── config/
│   └── db.php
│
├── api/
│   ├── login.php
│   ├── products.php
│   ├── order_create.php
│   ├── customers.php
│   └── report.php
│
├── assets/
│   ├── js/
│   │   ├── pos.js
│   │   ├── cart.js
│   │   └── scanner.js
│   │
│   ├── css/
│   │   └── app.css
│   │
│   └── libs/
│       └── jquery.min.js
│
├── components/
│   ├── product_card.php
│   └── cart_row.php
│
└── printer/
    └── receipt.php

2. MySQL Database

CREATE DATABASE pos_system;

USE pos_system;

CREATE TABLE users(
	id INT AUTO_INCREMENT PRIMARY KEY,
	name VARCHAR(100),
	username VARCHAR(100),
	password VARCHAR(255),
	role VARCHAR(50)
);

INSERT INTO users(name,username,password,role)
VALUES('Admin','admin',MD5('123456'),'admin');


CREATE TABLE products(
	id INT AUTO_INCREMENT PRIMARY KEY,
	barcode VARCHAR(50),
	name VARCHAR(200),
	price DECIMAL(10,2),
	stock INT,
	image VARCHAR(255)
);

CREATE TABLE customers(
	id INT AUTO_INCREMENT PRIMARY KEY,
	name VARCHAR(100),
	phone VARCHAR(20)
);

CREATE TABLE orders(
	id INT AUTO_INCREMENT PRIMARY KEY,
	customer_id INT,
	total DECIMAL(10,2),
	payment_type VARCHAR(20),
	cashier INT,
	created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE order_items(
	id INT AUTO_INCREMENT PRIMARY KEY,
	order_id INT,
	product_id INT,
	qty INT,
	price DECIMAL(10,2)
);

3. DB Connection

config/db.php

<?php

$host="localhost";
$user="root";
$pass="";
$db="pos_system";

$conn=new mysqli($host,$user,$pass,$db);

if($conn->connect_error){
 die("DB Failed");
}
?>

4. Login System

login.php

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <script src="assets/libs/jquery.min.js"></script>
    <link
      href="https://cdn.jsdelivr.net/npm/tailwindcss@2/dist/tailwind.min.css"
      rel="stylesheet"
    />
  </head>

  <body class="bg-gray-100 flex items-center justify-center h-screen">
    <div class="bg-white p-8 w-80 shadow">
      <h2 class="text-xl mb-4 font-bold">POS Login</h2>

      <input
        id="username"
        class="border p-2 w-full mb-2"
        placeholder="Username"
      />

      <input
        id="password"
        type="password"
        class="border p-2 w-full mb-4"
        placeholder="Password"
      />

      <button id="login" class="bg-blue-500 text-white w-full p-2">
        Login
      </button>
    </div>

    <script>
      $("#login").click(function () {
        $.post(
          "api/login.php",
          {
            username: $("#username").val(),
            password: $("#password").val(),
          },
          function (res) {
            if (res == "ok") {
              location = "dashboard.php";
            } else {
              alert("Login Failed");
            }
          }
        );
      });
    </script>
  </body>
</html>

5. Barcode Scanner Support

Most USB barcode scanners work as keyboard input.

Example JS:

scanner.js

let barcode = "";

$(document).keypress(function (e) {
  if (e.which == 13) {
    searchBarcode(barcode);

    barcode = "";
  } else {
    barcode += String.fromCharCode(e.which);
  }
});

function searchBarcode(code) {
  $.getJSON("api/products.php?barcode=" + code, function (p) {
    addToCart(p);
  });
}

6. POS Dashboard Layout

dashboard.php

<div class="grid grid-cols-12 h-screen">
  <!-- product list -->

  <div class="col-span-8 p-4 overflow-y-scroll">
    <input
      id="search"
      placeholder="Search product"
      class="border p-2 w-full mb-3"
    />
    <div id="products" class="grid grid-cols-4 gap-3"></div>
  </div>

  <!-- cart -->
  <div class="col-span-4 bg-white border-l p-4">
    <h2 class="font-bold text-xl">Cart</h2>
    <div id="cart"></div>
    <div class="mt-4">
      <p class="text-xl">Total: <span id="total">0</span></p>
      <button id="pay" class="bg-green-500 text-white p-3 w-full mt-3">
        Checkout
      </button>
    </div>
  </div>
</div>

7. POS Main JS

pos.js

let cart = [];

function loadProducts() {
  $.getJSON("api/products.php", function (data) {
    let html = "";

    data.forEach((p) => {
      html += `
<div class="bg-white shadow p-3 cursor-pointer product"
data-id="${p.id}"
data-name="${p.name}"
data-price="${p.price}">

<h3 class="font-bold">${p.name}</h3>
<p>${p.price}</p>

</div>
`;
    });

    $("#products").html(html);
  });
}

loadProducts();

$(document).on("click", ".product", function () {
  let id = $(this).data("id");
  let name = $(this).data("name");
  let price = $(this).data("price");

  let item = cart.find((i) => i.id == id);

  if (item) {
    item.qty++;
  } else {
    cart.push({ id, name, price, qty: 1 });
  }

  renderCart();
});

function renderCart() {
  let html = "";
  let total = 0;

  cart.forEach((c) => {
    total += c.qty * c.price;

    html += `<div class="flex justify-between border-b py-2">
        <span>${c.name} x${c.qty}</span>
        <span>${c.qty * c.price}</span>
        </div>`;
  });

  $("#cart").html(html);
  $("#total").text(total);
}

8. Checkout API

api/order_create.php

<?php

include "../config/db.php";

$data=json_decode(file_get_contents("php://input"),true);

$total=$data['total'];
$items=$data['items'];
$cashier=1;

$conn->query("INSERT INTO orders(total,cashier) VALUES('$total','$cashier')");

$order_id=$conn->insert_id;

foreach($items as $item){
    $id=$item['id'];
    $qty=$item['qty'];
    $price=$item['price'];
    $conn->query("INSERT INTO order_items(order_id,product_id,qty,price)
    VALUES('$order_id','$id','$qty','$price')");
    $conn->query("UPDATE products SET stock=stock-$qty WHERE id=$id");
}

echo json_encode(["status"=>"ok"]);

9. Receipt Printer

Thermal printers normally support ESC/POS.

Basic HTML receipt:

printer/receipt.php

<?php

include "../config/db.php";

$id=$_GET['id'];

$order=$conn->query("SELECT * FROM orders WHERE id=$id")->fetch_assoc();

$items=$conn->query("SELECT * FROM order_items WHERE order_id=$id");

?>

<h2>STORE NAME</h2>

Order #<?= $id ?>

<hr>

<?php while($i=$items->fetch_assoc()): ?>

  <?= $i['qty'] ?> x <?= $i['price'] ?>
  <br>

<?php endif; ?>

<hr>

Total: <?= $order['total'] ?>

<script>
    document.addEventListener("DOMContentLoaded", function(event) {
        window.print();
    });
</script>

10. PWA Manifest

{
  "name": "POS System",
  "short_name": "POS",
  "start_url": "/dashboard.php",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#000000"
}

11. Service Worker

self.addEventListener("install", (e) => {
  e.waitUntil(
    caches.open("pos").then((cache) => {
      return cache.addAll(["/", "/dashboard.php"]);
    })
  );
});

Professional Features You Should Add

To make it real commercial POS, add:

Inventory

  • stock alerts
  • supplier management

Sales

  • refunds
  • discounts
  • tax
  • coupon

Hardware

  • barcode scanner
  • receipt printer
  • cash drawer

Reports

  • daily sales
  • cashier sales
  • product sales

Performance

  • product search index
  • offline IndexedDB
  • background sync

To make it more ADVANCED ENTERPRISE POS architecture, you can includes-

  • offline-first PWA
  • WebSocket live inventory
  • 10k products instant search
  • touchscreen UI
  • barcode camera scanning
  • real-time dashboard
  • multi-store POS

If you want, We can build POS for your woocommerce store. Contact for build POS Today