Work/OnCloudWine
Wine club software that started as a WordPress plugin and outgrew it.
Wineries run their clubs on a cycle: build a release, let members tweak their selection, charge every card on file, then pack and ship hundreds of orders. Most do it with spreadsheets, a POS and a lot of manual card processing. OnCloudWine does the whole loop, and I have built every version of it.
- Product
- OnCloudWine
- Industry
- Wine and hospitality
- Services
- Product design, SaaS development, integrations
- Stack
- Next.js, TypeScript, PostgreSQL, Prisma, BullMQ, Redis
- Timeline
- October 2024 to now

The problem
A wine club looks simple from the outside: members pay, wine arrives. Behind it, a winery is juggling several club tiers, members who pause or cancel, members who want two more bottles of the Pinot and none of the rosé, cards that have expired since the last release, and a shipping run that has to match exactly what was charged. Before OnCloudWine, a typical winery exported its member list to a spreadsheet, keyed each card into the POS one at a time, and spent the next week fixing declines and mismatched shipments by hand.
The tools wineries already use don't think in releases. A POS knows about customers and orders. A WooCommerce store knows about products. Neither knows that the Autumn release goes to three clubs, that it charges 1,100 cards on one afternoon, and that a member in Tasmania picked up instead of shipping.
Version one: a WordPress plugin
The first OnCloudWine, built from October 2024, lived inside wp-admin. A React app on top of WooCommerce, where each club was a WordPress user role, products came from the store catalogue, and a release walked through five steps: create, select products, create orders, process cards, archive. Square handled the cards.
It worked for one winery on one site. It also showed me the limits quickly: the data model was WordPress's, every winery needed its own install, and the slow parts (charging hundreds of cards, generating packing lists) were running inside PHP requests and WP cron.

What I built next
In mid 2025 I rebuilt it as a standalone multi-tenant platform: a Next.js and TypeScript monorepo with a staff dashboard, a member portal, a public API, a marketing and docs site, and a BullMQ worker on Redis. Every table is scoped to an organisation, and each winery can run the dashboard and portal on its own domain.
The WordPress plugin didn't go away. I rewrote it as a thin client of the public API, so a winery's WooCommerce site can show members their upcoming release and let them change their selection, while the data lives in one place.
- 1Contacts CRM with tags, tasks, task automations (birthdays, anniversaries), discounts and saved cards.
- 2Clubs and memberships with active, on hold and cancelled states, signup locations and goals.
- 3A release wizard: products and quantities, which items are required or adjustable, target clubs, shipping or pickup.
- 4Member customisation from the portal, the WooCommerce plugin or by staff, before anything is charged.
- 5Batch payments through Square or the winery's own Stripe account, with failures turned into follow-up tasks and card update emails.
- 6Fulfilment: packing lists, FedEx and Shippo labels, VinoShipper for compliant US shipping, tracking, and product swaps with automatic charge or refund of the difference.
- 7Syncs with Square, WooCommerce and Mailchimp, plus Stripe billing for the wineries themselves.
The hard part: charging a thousand cards exactly once
Processing a release is the moment the product earns its keep, and the one place a bug costs real money. Staff walk through a wizard, confirm with a one-time code sent to their email, and a background job charges every eligible member. The database allows exactly one payment row per contact per release, so a retry reuses the row and a completed payment is never charged twice. Every Square charge carries an idempotency key built from the organisation, release, contact, amount and currency, and every Stripe charge carries its own key per contact, so a worker that dies halfway through can be rerun safely.
The platform fee is the other subtle bit. On Stripe it rides on the charge as an application fee. On Square it can't, so each fee is recorded in a ledger and added to the winery's monthly Stripe invoice. Around that sit encrypted OAuth tokens for every provider, a Square token refresh that runs both on demand and on a 12 hour schedule, and progress reporting so staff can watch a run of 1,100 payments land.

Where it is now
The platform has been in production since September 2025 and now 34 wineries run their clubs on it. As one winery's club manager put it, release day used to take the whole team two days, and now it is an afternoon with time left over to answer member emails.
In August 2026 I started moving it onto a newer foundation: Next.js 16, tRPC, Better Auth and Prisma 7, ported one domain at a time with a written migration log. The rebuild adds what the first platform was missing, a proper test suite: 130 unit tests, 238 database tests and 32 Playwright specs so far, plus a rerun of the unit suite in Sydney and Los Angeles time zones. That run alone caught four bugs where a release date shifted by a day.
More work
All case studiesPlugin, WooCommerce and Square
Square Sync for Woo
My WordPress plugin that keeps products, stock, orders and customers in step between WooCommerce and Square, both directions. It comes with its own webhook relay and licensing platform, and has shipped 416 releases.
Plugin, 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.