Work/Square Sync for Woo
A WooCommerce and Square sync that has to be right, because it moves real stock and real money.
Square Sync for Woo is a plugin I build, sell and support. It keeps a WooCommerce store and a Square point of sale agreeing on products, stock, orders, customers, gift cards and loyalty, in real time and in both directions. Behind it sit two more systems I run: a webhook relay that delivers Square events to every customer site, and the licensing and update platform at squaresyncforwoo.com.
- Product
- Square Sync for Woo
- Industry
- Retail and hospitality, WooCommerce stores on Square POS
- Services
- Product design, plugin development, infrastructure, support
- Stack
- PHP, React, Node.js, Fastify, BullMQ, PostgreSQL, Redis, Next.js
- Timeline
- 2021 to now, 416 releases

The problem
Plenty of businesses sell in a shop through Square and online through WooCommerce. The two systems each think they own the stock count. Sell the last bag of coffee at the counter and the website will happily sell it again unless something tells it, quickly and correctly.
The obvious fixes break in boring ways. Square webhooks go missing when a site is slow or a host blocks the request. WP cron only runs when someone visits. Square rate limits bulk work. And every sync that writes back risks an echo: a Woo edit goes to Square, Square fires a webhook, and the change comes straight back as a second update or, worse, a duplicate order.
What I built
A WordPress plugin with a React admin app inside wp-admin and a PHP backend built on Action Scheduler queues, plus the services it depends on. Merchants choose which fields each side owns, and the plugin enforces that ownership on every import, export and webhook.
- 1Two-way product, stock and price sync, with variations, categories, images, modifiers, custom attributes and decimal (yardage) stock.
- 2Order sync both ways: online orders pushed to Square with payment, POS sales imported into WooCommerce, with order splitting across locations, pickup and delivery.
- 3A Square payment gateway for card, Apple Pay, Google Pay, Afterpay and Cash App, plus gift cards and Square Loyalty at checkout.
- 4Customer sync with Square group to WordPress role mapping, and agency permissions so a developer can hand a client a restricted view.
- 5A sync log that records every event with its direction, the objects involved and a retry button.
- 6A webhook relay in Node.js (Fastify, BullMQ, PostgreSQL, Redis) that receives Square events once and delivers them to each licensed site.
- 7The marketing, docs, licensing and update platform: a Next.js and Turborepo monorepo with Stripe billing, a licence API, signed update manifests and tagged releases deployed by GitHub Actions.

The hard part
Most of the work is in the failures that make no noise. A stock push that quietly does nothing, a gift card charged as a $0 payment, a product imported twice by two queue workers at once. Each looks like success in a naive log. I treat every one of those as a test: 51 integration suites run inside a real WordPress install with Square faked at the HTTP layer, so they assert on exactly what would have been sent to Square, not just on what WooCommerce ended up with.
Some concrete examples. Loop guards and push markers stop an order the site created in Square from being imported back as a second order. A cross-process lock per Square item means parallel workers never create the same product twice. Square's order search is cursor-paginated with no total, so the orders screen keeps pulling pages until a filtered view is genuinely full. And when Square rejects something, the log does not just show the error code: it records a plain-English cause and fix at the moment it happens, written only for errors I have actually diagnosed.

Webhooks that actually arrive
Square sends each event once. If a customer's site is down, slow or behind a firewall, that update is gone, and the shop drifts out of sync with nothing to show for it. So the plugin does not take webhooks from Square directly. My relay takes them, verifies the signature, stores them in PostgreSQL and delivers them through BullMQ with a per-site circuit breaker.
When a site fails three times in a row the circuit opens and retries back off at 1, 5, 15 and 60 minutes. Held events are kept rather than dropped, and replayed oldest first (up to 500 at a time) once the site responds. A fair scheduler stops one busy merchant starving everyone else, and stores running several sites are isolated by callback URL. Inside the plugin, merchants see all of this as one plain verdict, with the controls to test, resume or discard a backlog themselves instead of opening a ticket.
More work
All case studiesPlugin, WooCommerce and Vinoshipper
Vinoshipper for WooCommerce
A WooCommerce plugin that lets US wineries keep their own checkout and payment gateway, while orders to states they aren't licensed for are handed to Vinoshipper for compliance and shipping. Tracking comes back automatically.
SaaS, wine club management
OnCloudWine
Wine club software for wineries: members, clubs, releases, card payments and shipping in one place. It started as a WordPress plugin and grew into a multi-tenant platform with its own member portal and seven provider integrations.