Skip to main content

POS Sync

FREE!

Real-time POS sync in minutes.

Keep your online store's inventory in step with sales rung up at the till — a provider-agnostic bridge between any point-of-sale (Square, Clover, Zettle, Lightspeed) and any WordPress store (WooCommerce, FluentCart, SureCart, Easy Digital Downloads, Ecwid). Real-time stock sync, free.

POS Sync keeps an online store in step with the physical shop. When a customer buys the last blue hoodie at the counter, the website should stop selling it — within seconds, without anyone touching WordPress.

That sounds like one integration. It is really two independent problems, and the whole design follows from keeping them apart:

  • Which till? Square, Clover, Zettle, Lightspeed, or a shop's own bespoke software.
  • Which store? WooCommerce, FluentCart, SureCart, Easy Digital Downloads, Ecwid.

:::info Status — 1.1.0, roadmap complete The ledger is built (schema, idempotency, the event-time-ordered queue, retries, the audit log), so is the store driver seam with WooCommerce on the end of it, and so is the signed webhook API — which means a real till can feed the ledger today. Any POS with outbound webhooks, any middleware, or a shop's own software can post to it. 346 assertions cover them, needing neither hardware, a Square account, nor — for most of them — a cart.

The Virtual Terminal is built, so all of it can be exercised and demonstrated with no hardware at all — twelve adversarial scenarios, each checking its own expectation. And the first-party Square driver now ships: OAuth, webhooks, catalog import and location mapping.

Reconciliation and the operational layer are built too: a nightly sweep that asks the POS what it thinks the numbers are, an authority policy for who owns which field, and a health dashboard whose alarm is silence — because a till that stops sending does not throw, it just goes quiet while stock drifts.

The roadmap is complete. Three of the expansion drivers — FluentCart, SureCart and Clover — are marked experimental: they were written against documented APIs with no live install to verify against, so each refuses to activate unless every function it calls is actually present. A wrong assumption disables the driver; it cannot quietly write wrong stock. If one does not work for you, the Health screen produces a diagnostic report that is safe to paste in public.

The rest of this section is the design of record: the architecture, the wire format, and the build order. The Roadmap tracks progress and updates itself from the extension source — see How this page stays current.

Source: UnysonPlus-POS-Sync-Extension :::

Why it is its own extension​

It would be natural to add this to the WooCommerce extension. That turns out to be the expensive choice.

With N POS providers and M e-commerce plugins, POS logic living inside a Woo-specific extension means every provider driver has to be rewritten to add a second cart — N×M work. Put a normalized ledger in the middle instead, and each side only has to know about that ledger: N+M.

Square ─┐ ┌─ WooCommerce
Clover ─┤ ├─ FluentCart
Zettle ─┼─► POS drivers ─► LEDGER ─► store ──┼─ SureCart
Any │ drivers ├─ EDD
webhook ─┘ └─ …

The ledger is the canonical, cart-agnostic record of what happened in the shop: an item, a sale, a refund, a stock movement. POS drivers only write to it. Store drivers only read from it. Neither side knows the other exists.

Three practical consequences follow:

  • Scope stays clean. The WooCommerce extension is about storefront integration. POS sync is background jobs, webhook endpoints, HMAC secrets, reconciliation and an audit log — a different concern, and one that shouldn't load for every Woo user who will never own a till.
  • The hard problems are cart-agnostic anyway. Idempotency, out-of-order offline batches, refund restocking, location mapping — none of that is WooCommerce-shaped. Coupling it to wc_update_product_stock() buries generic logic inside one vendor's API.
  • Activation is independent. A FluentCart shop shouldn't need the WooCommerce extension active.

→ The full reasoning, including the trade-offs considered, is in Architecture.

What ships first​

Deliberately narrow. POS integrations are maintenance-heavy — every vendor is bespoke and their APIs move — so version 1 is one generic surface plus one polished driver, not five half-drivers.

WhatWhy first
1 ✅Generic webhook API ✅Any POS with outbound webhooks, any middleware (Zapier / Make / n8n), or a shop's own till software can push to it. You ship a spec, not an integration — nothing to break when a vendor changes their API.
2 ✅Virtual Terminal ✅Fires realistic synthetic sales at your own endpoint. Development test rig, customer pre-launch check, and live demo, all in one screen.
3 ✅WooCommerce store driver ✅The largest install base, and the reference implementation of the store seam.
4Square provider driver ✅Best free sandbox, cleanest API, biggest small-retail share. Proves the provider seam.

Everything else — FluentCart, SureCart, Clover, Zettle, Lightspeed — is post-1.0, added on demonstrated demand.

"But I don't own a POS machine"​

You don't need one. Every serious POS vendor publishes a free sandbox that emulates the hardware entirely: you create a test seller account, push a sale through their API, and receive the same real webhooks a physical terminal would send.

Add the Virtual Terminal for the cases sandboxes make awkward — duplicate webhooks, out-of-order offline batches, expired signatures — and hardware never enters the picture.

→ Testing without hardware walks the whole setup, including the tunnel that lets sandbox webhooks reach a local XAMPP install.

The decisions that matter most​

These bite harder than any API plumbing. Each is settled here so the implementation doesn't have to re-litigate it:

  • Stock has one owner. The POS is authoritative for stock levels; the store is authoritative for product content — title, description, images, SEO — with a per-product override. Symmetric two-way stock sync with no declared authority produces drift and oversells, reliably.
  • Match on SKU. Always. GTIN/barcode as a fallback. Never on product title.
  • Every event is idempotent. Webhooks get retried; tills reconnect and re-send. The external transaction id sits behind a UNIQUE index, so a replay is recorded and dropped, never applied twice.
  • Apply by event time, not arrival time. An offline till dumps its batch late and out of order. Ordering by arrival lets a stale sale overwrite a newer count.
  • Refunds, voids and partial returns restock. This is where cheap integrations fall over.
  • Locations are explicit. Each POS location maps to a stock source, or the site declares single-store mode. Never left implicit.
  • Architecture — the ledger, the two driver seams, and the data model. Start here.
  • Roadmap — what's built, what's next, verified against the source tree.
  • The webhook API — endpoints, signing, and the normalized payload schema.
  • Store drivers — the FW_POS_Store interface and each cart's notes.
  • Square — connecting Square, sandbox to production, step by step.
  • Testing without a POS — sandbox, tunnel, Virtual Terminal, automated suite.
  • Troubleshooting — when stock is wrong and you need to know why.