01The constraint
Harrow & Vine sell books, host events and run a membership scheme from nine branches across the South East. All three had been implemented separately: WooCommerce for online sales, an events plugin with its own ticket model, and a bespoke membership system. They shared a database and nothing else. Customers with a membership got an error roughly one time in twelve.
02What we did
We moved commerce to Shopify for catalogue, pricing, tax and checkout, and kept WordPress for everything an author or bookseller would want to write about: interviews, reading lists, staff picks, branch news. The events system was rebuilt on Shopify ticketing so a ticket and a book order could exist in the same basket. Membership moved to a server-side entitlement check against a token issued at login, so the discount follows the customer across both the editorial and the commerce surface without either system knowing about the other.
03What changed
Checkout conversion rose 41% against the WooCommerce build over twelve weeks. The membership error rate went to zero and has stayed there. Events revenue doubled year on year, which the team attributes mostly to being able to put a book in a ticket basket. The migration preserved every legacy URL: 4,182 mappings, all verified by crawl, no traffic lost.
In detail
Three systems, one customer
The core problem was not technical debt. It was that the customer had three identities and the systems did not know it. A member who booked an event and bought a book existed twice, and neither system could see the other purchase well enough to offer the other discount.
We made the customer the join rather than the consequence. Membership is an entitlement, not a store. Logged in, the visitor carries an entitlement token; the storefront reads it server-side and applies pricing at the Storefront API boundary, so the discount is real at checkout rather than cosmetic in the interface.
Why events became commerce
Ticket sales were doing something unusual: they were succeeding, at a lower margin, through a separate checkout with a different payment stack. When we moved them onto Shopify ticketing, the average basket rose from £14 to £31, almost entirely because attendees started buying the book they had come to hear about.
We had been running two shops on the same website for a decade and never once put a book in a ticket basket. That was not a technology failure. It was an architecture failure.
Migration without an incident
WooCommerce to Shopify is not a like-for-like import, so we built a translation layer rather than a migration script. Product records were mapped by SKU first and by fuzzy title match only where SKU was absent — 318 of 9,600 records — and every one of those was reviewed by a human before import.
Orders in progress at cutover were completed manually by the shop managers. There were eleven of them. Nobody noticed, which is the highest praise a migration can receive.