Building a professional WooCommerce POS PWA plugin is a large system, so the correct approach is to design a clean architecture first and then implement modules. Below is a production-ready structure and working base code that you can expand into a full POS.
Stack you requested:
- Frontend: TailwindCSS + jQuery
- Backend: PHP (WordPress plugin)
- Database: MySQL (via WordPress / WooCommerce tables)
- PWA: Service Worker + Manifest
- Features: POS UI, Cart, Barcode, Orders, Offline Sync
1. POS Plugin Architecture
Plugin folder:
woocommerce-pos-pwa/
│
├── woocommerce-pos-pwa.php
├── manifest.json
├── service-worker.js
│
├── includes/
│ ├── api.php
│ ├── auth.php
│ ├── orders.php
│ └── products.php
│
├── assets/
│ ├── css/
│ │ └── pos.css
│ ├── js/
│ │ ├── pos.js
│ │ ├── cart.js
│ │ ├── barcode.js
│ │ └── sync.js
│ └── icons/
│
└── templates/
└── pos-ui.php
2. WordPress Plugin Main File
woocommerce-pos-pwa.php
<?php
/*
Plugin Name: WooCommerce POS PWA
Description: Progressive Web App POS system for WooCommerce
Version: 1.0
Author: NeedleCode
*/
if (!defined('ABSPATH')) exit;
class WC_POS_PWA {
public function __construct() {
add_action('admin_menu', [$this,'menu']);
add_action('admin_enqueue_scripts', [$this,'assets']);
add_action('rest_api_init', function () {
register_rest_route('pos/v1','products',[
'methods'=>'GET',
'callback'=>['WC_POS_PRODUCTS','get_products']
]);
register_rest_route('pos/v1','order',[
'methods'=>'POST',
'callback'=>['WC_POS_ORDERS','create_order']
]);
});
}
function menu(){
add_menu_page(
'POS',
'POS',
'manage_woocommerce',
'wc-pos',
[$this,'pos_page'],
'dashicons-cart'
);
}
function pos_page(){
include plugin_dir_path(__FILE__).'templates/pos-ui.php';
}
function assets(){
wp_enqueue_script('jquery');
wp_enqueue_script(
'pos-js',
plugin_dir_url(__FILE__).'assets/js/pos.js',
['jquery'],
'1.0',
true
);
wp_enqueue_style(
'tailwind',
'https://cdn.jsdelivr.net/npm/tailwindcss@3/dist/tailwind.min.css'
);
wp_localize_script('pos-js','POSAPI',[
'url'=>rest_url('pos/v1/')
]);
}
}
new WC_POS_PWA;
require_once plugin_dir_path(__FILE__).'includes/products.php';
require_once plugin_dir_path(__FILE__).'includes/orders.php';3. Product API
includes/products.php
<?php
class WC_POS_PRODUCTS {
public static function get_products(){
$args = [
'limit'=>100,
'status'=>'publish'
];
$products = wc_get_products($args);
$data = [];
foreach($products as $product){
$data[]=[
'id'=>$product->get_id(),
'name'=>$product->get_name(),
'price'=>$product->get_price(),
'stock'=>$product->get_stock_quantity(),
'image'=>wp_get_attachment_url($product->get_image_id()),
'barcode'=>$product->get_sku()
];
}
return $data;
}
}4. Create Order API
includes/orders.php
<?php
class WC_POS_ORDERS {
public static function create_order($req){
$items = $req['items'];
$payment = $req['payment'];
$order = wc_create_order();
foreach($items as $item){
$product = wc_get_product($item['id']);
$order->add_product(
$product,
$item['qty']
);
}
$order->set_payment_method($payment);
$order->calculate_totals();
$order->save();
return [
'status'=>'success',
'order_id'=>$order->get_id()
];
}
}5. POS UI (Responsive)
templates/pos-ui.php
<div class="flex h-screen">
<!-- PRODUCTS -->
<div class="w-2/3 p-4 overflow-y-scroll">
<input id="search" placeholder="Search product" class="border p-2 w-full mb-4" />
<div id="products" class="grid grid-cols-2 md:grid-cols-4 gap-4"> </div>
</div>
<!-- CART -->
<div class="w-1/3 bg-gray-100 p-4">
<h2 class="text-xl font-bold mb-4">Cart</h2>
<div id="cart"></div>
<div class="mt-4">
<button id="checkout" class="bg-green-500 text-white w-full p-3"> Checkout </button>
</div>
</div>
</div>6. Load Products
assets/js/pos.js
let cart = []
function loadProducts(){
$.get(POSAPI.url+"products",function(data){
let html=''
data.forEach(p=>{
html+=`
<div class="border p-2 cursor-pointer"
onclick="addCart(${p.id},'${p.name}',${p.price})">
<img src="${p.image}" class="h-24 mx-auto">
<div class="text-center">
${p.name}
</div>
<div class="text-center text-green-600">
${p.price}
</div>
</div>
`
})
$("#products").html(html)
})
}
loadProducts()7. Cart System
assets/js/cart.js
function addCart(id,name,price){
let item = cart.find(i=>i.id==id)
if(item){
item.qty++
}else{
cart.push({
id:id,
name:name,
price:price,
qty:1
})
}
renderCart()
}
function renderCart(){
let html=''
let total=0
cart.forEach(i=>{
total+=i.price*i.qty
html+=`
<div class="flex justify-between border-b py-2">
<div>${i.name} x ${i.qty}</div>
<div>${i.price*i.qty}</div>
</div>
`
})
html+=`<div class="font-bold mt-3">Total: ${total}</div>`
$("#cart").html(html)
}8. Checkout
$("#checkout").click(function(){
$.ajax({
url:POSAPI.url+"order",
method:"POST",
contentType:"application/json",
data:JSON.stringify({
items:cart,
payment:"cash"
}),
success:function(res){
alert("Order "+res.order_id+" created")
cart=[]
renderCart()
}
})
})9. Barcode Scanner
Use keyboard barcode scanner (most POS scanners work as keyboard input).
let barcode=''
$(document).keypress(function(e){
if(e.which==13){
searchBarcode(barcode)
barcode=''
}else{
barcode+=String.fromCharCode(e.which)
}
})
function searchBarcode(code){
$.get(POSAPI.url+"products",function(data){
let p=data.find(x=>x.barcode==code)
if(p){
addCart(p.id,p.name,p.price)
}
})
}10. PWA Manifest
manifest.json
{
"name":"WooCommerce POS",
"short_name":"POS",
"display":"standalone",
"start_url":"/wp-admin/admin.php?page=wc-pos",
"background_color":"#ffffff",
"theme_color":"#000000",
"icons":[
{
"src":"icons/icon-192.png",
"sizes":"192x192",
"type":"image/png"
}
]
}11. Service Worker (Offline)
service-worker.js
const CACHE = "pos-cache"
self.addEventListener("install", e => {
e.waitUntil(
caches.open(CACHE).then(cache => {
return cache.addAll([
"/",
"/manifest.json"
])
})
)
})
self.addEventListener("fetch", e => {
e.respondWith(
caches.match(e.request)
.then(res => {
return res || fetch(e.request)
})
)
})12. Receipt Printing
function printReceipt(order){
let w=window.open()
w.document.write(`
<h2>Receipt</h2>
Order #${order.id}
`)
w.print()
}13. Offline Sync Logic
assets/js/sync.js
function saveOffline(order) {
let orders = JSON.parse(localStorage.getItem("offline_orders") || "[]")
orders.push(order)
localStorage.setItem("offline_orders", JSON.stringify(orders))
}
function syncOrders() {
let orders = JSON.parse(localStorage.getItem("offline_orders") || "[]")
orders.forEach(o => {
$.post(POSAPI.url + "order", o)
})
localStorage.removeItem("offline_orders")
}
window.addEventListener("online", syncOrders)14. Professional POS Features To Add
Real production POS should include:
- Product search
- Category filter
- Discount system
- Tax calculation
- Customer selection
- Multiple payment types
- Receipt printer support
- WebSocket stock sync
- Inventory update
- Multi-cashier login
- Shift management
- Sales dashboard
- IndexedDB offline storage
15. Professional POS UI Layout
--------------------------------------
| Products Grid | Cart |
| | |
| | Order Summary |
| | Payment Buttons |
--------------------------------------
| Barcode Input | Customer | Total |
--------------------------------------
💡 Since we are building professional POS system, the next level would be:
- Realtime stock sync
- IndexedDB offline database
- Camera barcode scanner
- Thermal printer support
- Multi-store POS
- 10k product performance
If you want, We can build POS for your woocommerce store. Contact for build POS Today