An online store with real-time ERP sync, the kind of project agencies usually sell as a separate expensive engagement with a mandatory Bitrix license attached, cost this client zero dollars in licenses. It runs on WordPress and WooCommerce, and most of the code was written by Claude Code, not by me. If you assume ERP integration for a Russian retailer automatically means Bitrix and an agency-sized budget, this case is worth a second look.
Short version, if you want the summary instead of the full story: a designer clothing store on WordPress plus WooCommerce, with real-time stock and price sync to a 1C:Retail ERP over the CommerceML 3.1 protocol. The design is an exact reproduction of an existing React prototype, built as a custom block theme (FSE) with zero page builder plugins. Backend on PHP 8, variable products with sizes and colors, YooKassa and SBP payments, CDEK delivery, 152-FZ privacy compliance. Most of the code was written by the Claude Code AI agent, with a human designing the architecture, setting tasks, and verifying results through live Playwright browser tests. Budget: zero dollars in licenses, on an open source stack instead of a boxed platform.
The client showed up with what sounded like a simple brief. A women's clothing brand out of Yekaterinburg, Russia, running inventory in 1C:Retail (think QuickBooks and Lightspeed welded into one very corporate product that only talks to the outside world through XML, if you have never met it). The design was already done, and the mandate was to reproduce it exactly, not "roughly match it." The budget drew a hard line too. No four-figure annual platform licenses, no six-person team. One developer, an open source stack, and a deadline that was politely called tight but really just meant not much time.
Here is what the rest of this piece covers: the real stack with exact versions and integration boundaries for anyone who wants to reproduce this, the 1C traps nobody documents, an honest list of what the AI agent does well and badly, and a scannable troubleshooting section near the end. If you are here for specifics rather than an inspirational story, that section is probably the most useful part of the page.
The problem: a boutique store without a boutique budget
In Russia, say "we need a store synced with 1C" out loud and you will hear the same answer every time: buy Bitrix. It is the dominant commercial CMS in that market, it ships with a stock 1C exchange module out of the box, boxed pricing tiers, and a deep bench of integrators who have done this a hundred times. The closest US analogy is getting steered toward Adobe Commerce because that is what the agencies already know how to bill for. Fair enough as defaults go. But does that logic actually hold for a one-location clothing boutique?
It does not, and it breaks in exactly two places: money and design.
Money first. The Bitrix license, the annual renewals, the integrator's invoice: stack them up and you get a budget on par with this entire open source build. For a single-location boutique that is not a cost, it is overpaying.
Design is the harder one. The client did not want "a theme with a catalog bolted on," they wanted the prototype rebuilt exactly: a four-color palette, Lora and Raleway typography, zero border radii, an airy grid. You can bend a stock theme into that shape. The question is at what cost, and the honest answer is it takes longer than writing your own theme from zero.
None of the requirements shrank to fit the budget, either: real-time stock from 1C:Retail (clothing means variable products, and every size-color combination carries its own stock count), payments through YooKassa with SBP support (the local card processor and instant-payment rail, roughly Stripe's slot in the US), CDEK courier delivery, a cookie banner with personal-data consents under 152-FZ (Russia's rough equivalent of GDPR), and ecommerce events wired into Yandex Metrica.
Add it up and you get a set of conditions that reads like a contradiction on paper: full-grown store functionality, a minimal budget, and a custom design, all required at once, no picking two out of three. The bet was on a combination of exactly two things: a free WordPress plus WooCommerce stack, and an AI agent standing in for a team.
The solution
Here is the full stack up front, and I will unpack each piece below:
- CMS: WordPress with a fully block-based theme (FSE) built from scratch, no page builder plugins
- E-commerce: WooCommerce + WooCommerce Blocks, mini cart through the Store API
- Backend: PHP 8, a modular
inc/structure, ACF with fields registered in code rather than through the UI - Frontend: ES modules with no jQuery,
IntersectionObserverfor animations,history.pushStatefor filter state, Lucide icons as an inline SVG sprite - Design tokens:
theme.jsonas the single source, CSS variables generated from it - ERP sync: 1C:Retail over CommerceML 3.1, real time
- Payments and services: YooKassa (cards and SBP), CDEK delivery, Unisender for email, Yandex Metrica with ecommerce events
- Hosting and deploys: NetAngels, deploy over SSH
- AI tooling: Claude Code plus MCP servers (Playwright for in-browser checks, SSH for deploys)
Versions and integration boundaries
For anyone who wants to reproduce this rather than just read about it, here are the exact versions as of publication:
- WordPress 7.0.2, the theme requires 6.6 or newer
- WooCommerce 10.7.0
- PHP 8.2, the theme explicitly requires it and will not build on anything older
- ACF (Advanced Custom Fields) 6.8.3, fields registered in code, not through the UI
- 1C sync: the open source plugin "Data Exchange for WooCommerce and 1C:Enterprise" (by Danil Semelenov), version 0.9.20, riding on top of CommerceML 3.1
- 1C:Retail itself: the release is the client's call, and I am deliberately not pinning a platform version here, because it is not something the site developer controls
It is worth stating plainly what the exchange plugin actually does and does not solve, so you do not over-trust the box. It parses XML packets and updates the catalog, stock, and prices. It does not: automatically recalculate product counts on attribute terms after import (covered below, in troubleshooting), resolve conflicts if the same product gets edited on the site and in 1C at the same time, or validate the data coming out of 1C. Garbage in stays garbage out unless you add your own validation layer.
Stack and architecture: a block theme as a design system
The foundation is WordPress with a fully block-based theme, FSE, and not a single page builder plugin in sight. On a budget project that is not a preference, it is a necessity: any builder like Elementor costs you performance, chains you to someone else's release schedule, and drops an extra layer between the design and the code. Cutting that corner was never really an option.
So instead, the entire design system from the React prototype moved into theme.json, which became the one source of truth for every token: a four-color palette, an 8px spacing scale, two typefaces. WordPress generates the CSS variables from that on its own, and everything downstream references only the tokens, never a raw value:
:root {
--color-bone: /* background, ~70% of the canvas */;
--color-ink: /* text and UI */;
--color-bordeaux: /* sale and accents */;
--color-pine: /* dark navigation */;
}Text hierarchy rests on transparent variants of the main color rather than a pile of ten grays. Radii sit at zero everywhere except the pill-shaped tags. Jumping ahead a bit: this rigid rule set earns its keep later, in the AI part of the project, and here is why.
Even hard rules do not cover everything, though. At one point the background token changed on the client's call, from a warm tone to a neutral one. The "no raw hex" rule held perfectly for every new value. It did not hold for fallbacks. A few spots in the PHP that renders product swatches carried a hardcoded backup color for whenever a product had no color coming from 1C, and nobody touched it, because on paper it was not "a new decision," just an old default. Nobody caught it right away either: swatches on colorless products kept rendering in the old shade against the new background. The lesson is simple and a little uncomfortable: a rule against raw values has to cover defaults too, not only visible new decisions, or the gap sits exactly where nobody thinks to look.
I kept the PHP layer deliberately plain. functions.php does nothing on its own, it just wires up modules from inc/, one responsibility per file:
// functions.php: wiring only, no logic
require_once get_theme_file_path( 'inc/setup.php' );
require_once get_theme_file_path( 'inc/assets.php' );
require_once get_theme_file_path( 'inc/woocommerce.php' );
require_once get_theme_file_path( 'inc/ajax-handlers.php' );Repeating sections live in block patterns, chrome like the header and footer lives in template parts. I overrode WooCommerce templates one at a time, only where the stock markup actually fought with the design.
The AI workflow: what vibe coding looks like on a real project
The agent did not build this store on its own, that is not how it works. It wrote most of the code, and I did the architecture, set the tasks, and signed off on the results. The workload did not shrink. What changed was throughput.
The workflow rests on three things.
First: a contract in CLAUDE.md, the instruction file Claude Code rereads at the start of every single session. I put the whole design system in there as flat prohibitions: exactly four colors in the palette, raw hex codes banned outright, radius fixed at zero, headings in uppercase with letter spacing, spacing pulled only from the 8px scale. Give an LLM an inch and it will happily improvise. Hard rules kill that instinct dead: when the agent needs a new shade, it does not type a hex value, it derives one with oklch() from an existing token.
Second: file-based memory across sessions. The agent's context window is finite, and this project ran for months. Every lesson that cost real time ("WooCommerce Blocks does not load jQuery," "1C delta exports wipe out variations") went straight into markdown memory files. A fresh session does not start from zero, it starts from a digest of every trap already stepped in. Call it an engineer's external long-term memory. The only difference is the one reading it is an agent, not a person.
Third: verification in a live browser instead of by eyeballing the code. The agent gets Playwright through MCP, so it opens the page itself, clicks around, takes a screenshot, and checks it against the prototype. The rule is strict: the phrase "bug fixed" does not count without a live run to back it up. That rule earned its place on the product-card incident. Clicks on the links were dead, but only on desktop, and only in production, which by itself should tell you something is off. The agent reproduced the bug with a real Playwright click and dug out the cause: setPointerCapture in the carousel was hijacking pointer events and retargeting the click from the link to the carousel track itself. No amount of eyeballing was going to catch that. The carousel code read as completely correct.
Now the part worth being honest about, not just the praise. Left alone, the agent will confidently "fix" the wrong cause unless you make it reproduce the bug first. It drags in solutions that ignore the design system the moment the rules go the least bit fuzzy. It loses the thread on long tasks with no external memory to fall back on.
One concrete example of that last failure mode: the agent once tied a piece of menu logic to a literal numeric WordPress menu-post ID instead of resolving it through the registered theme location. It worked fine until content got re-exported and the ID shifted, and then the menu quietly stopped highlighting the right item, with nothing in the logs to flag it. Same class of problem as the fallback color from the earlier section: code that is technically working but brittle, and a blanket "do not hardcode" rule does not save you unless you spell it out for every specific spot, post IDs included, not just colors and spacing.
All of this gets solved by process, not by prompting, which is exactly why this piece leans so hard on process instead of just code.
How to deploy a WooCommerce store with Claude Code
Short answer: Claude Code does not deploy anything on its own. You give it access through MCP servers, one for SSH to the hosting and one for a real browser through Playwright, write the rules into CLAUDE.md, and treat a deploy as unfinished until the agent has opened the live page and shown you it works.
The order matters, so this is how it went on this store.
- Write CLAUDE.md before the first line of code. The agent rereads it at the start of every session. Mine held the design system as hard bans: four colors, no raw hex, zero radius, the 8px scale. Anything you leave out of that file, the agent decides for you, and usually not the way you would.
- Give it the server through an SSH MCP server. That is how the agent ships code to the hosting (NetAngels in my case) instead of me copying files around. It also means the agent can look at the server itself instead of asking you to paste logs, which matters for bugs that only exist in production.
- Give it eyes through a Playwright MCP server. Without a browser the agent can only reread its own code and tell you it looks right. With one, it opens the store, clicks the real buttons, and compares a screenshot against the prototype.
- Make the live check part of "done". My rule: "deployed" or "fixed" means nothing until a Playwright run on the live page backs it up. The dead product-card links from the section above only showed up on desktop and only in production. A code review would have shipped that bug.
- Write every trap into memory files. The project lasted months, the context window does not. Each lesson that cost me time went into a markdown file the next session reads first, so a fresh session starts from what we already know.
- Keep architecture and acceptance for yourself. The agent wrote most of the code. What to build, in what order, and whether the result was good enough to show the client stayed with me.
Connecting WooCommerce to Claude Code: what that actually means
People search for this as if there were a plugin that wires the store into the agent. Look at the tooling list at the top of this section: two MCP servers, Playwright and SSH, and nothing WooCommerce-specific. Claude Code worked with the theme code, reached the hosting over SSH, and saw the store the way a customer does, through a browser.
That was enough, because the parts that ate the most time (the 1C exchange, the variation logic, the block-theme wrappers) were code and data problems. Having an agent manage products or orders inside WooCommerce directly is a different job, and it is outside this case.
The 1C integration: CommerceML and the traps the manuals keep quiet about
This is the most expensive part of the project by the hour, and, I suspect, the most useful section for anyone about to wire an ERP or POS system into WooCommerce over an XML exchange. Fair warning: it gets dry and technical fast.
The scheme looks simple on paper: 1C:Retail exports XML packets on a schedule and on events, real-time mode switched on, and a WordPress-side plugin parses them and updates the catalog. The traps, as always, live in the details, not the diagram.
One note before the traps: CommerceML is a Russian national standard, so the XML element names below are genuinely in Russian. That is not obfuscation, that is the protocol.
Trap one: stock counts that were not actually there. The storefront showed wrong numbers while the exchange completed cleanly, not a single error logged, which raises the obvious question: how do you get bad data out of a process reporting zero errors? The cause only surfaced once I diffed the raw XML against what actually landed in the database. In CommerceML 3.1, stock arrives in separate rests_*.xml files with a nested structure, split per warehouse inside <Остатки><Остаток><Склад>:
<Предложение>
<Ид>product-guid#variation-guid</Ид>
<Остатки>
<Остаток>
<Склад>
<Ид>warehouse-guid</Ид>
<Количество>3</Количество>
</Склад>
</Остаток>
</Остатки>
</Предложение>The plugin's parser was waiting for a flat <Количество> one level up and quietly skipped the nested version. No errors, no warnings, the exchange looks perfectly healthy, and the data is simply wrong. A parser patch fixed it. Ever since, I diff the plugin's code before every update, because the next release can silently bring the old behavior right back and nothing will flag it.
Trap two: two attribute pipelines that do not talk to each other. In CommerceML, item properties and variation characteristics are separate mechanisms, and the plugin runs each through its own code path. A color entered as a property lands in WooCommerce differently than a size entered as a characteristic. Until you trace both pipelines by hand, step by step, the outcome is anyone's guess.
Trap three: delta exports at war with variations. The fast exchange ships delta chunks, not the full catalog. On every chunk the plugin rebuilt the product's whole variation set from scratch, and any variation absent from that particular chunk just vanished. The size grid quietly thinned out with no obvious cause, which was disorienting the first few times it happened. The fix was a mode that preserves variations on partial exports, plus a one-time database cleanup: roughly a thousand orphaned variations had piled up by the time I noticed.
Trap four: GUIDs where slugs should be. Attribute terms were created with GUID slugs, which broke filter URLs and left the admin screen unreadable. Fixed with mapping constants plus a repair script that runs after every exchange.
Worth telling separately is a case that was not a trap at all, it was a decision, not a bug. For some products, 1C sends the color as a single string covering two colors at once, something like "white / green," simply because that is how the characteristic is set up on the inventory side. You can technically split a string like that into two separate attributes. But that is a data decision, not a parsing one, and it is not mine to make quietly in code, so I brought it to the client. The answer surprised me: do not split it into two terms, render one swatch split diagonally between the two colors instead. Not the most obvious technical call, but a precise one, and it came from someone who actually understands the merchandise. The takeaway: not every oddity in 1C data is a bug to quietly patch. Sometimes it is a question to ask, not a decision to make on the client's behalf.
The short version: an ERP-to-WordPress sync does run reliably in production, ours runs in real time, in fact. But drop the "install the plugin and walk away" fantasy right now. You will read the plugin's code, patch the parser, and write your own verification scripts. Budget the time for that up front, not after it blows the deadline.
Payments, shipping, and the compliance layer
The local services layer is assembled from the usual blocks: YooKassa with cards and SBP for payments, CDEK and the state postal service for delivery, Unisender for email, Yandex Metrica ecommerce events for analytics. Swap in Stripe, ShipStation, Mailchimp, and GA4 and the shape of the work barely changes. The line item budget estimates keep forgetting, though, is privacy compliance: 152-FZ here, GDPR or CCPA somewhere else. A cookie banner, consent checkboxes on every form, terms at checkout. None of it is technically hard. All of it tends to surface in the final week and push the deadline if nobody planned for it from day one.
A frontend without jQuery: a surprise from WooCommerce Blocks
Modern WooCommerce with block templates does not load jQuery on the storefront. None. The classic tutorial snippet, $('.variations_form').on('found_variation', …) and its relatives, has nothing left to run on, which caught me off guard partway through the build. For a block theme I ended up writing a native variation resolver in plain JS: gather the selected attributes, find the matching entry in the variation data, update price, photo, and availability:
// No jQuery here: WooCommerce Blocks doesn't load it.
const match = variations.find((v) =>
Object.entries(selected).every(
([attr, value]) => !v.attributes[attr] || v.attributes[attr] === value
)
);More of the same flavor: AJAX catalog filters render straight into the block product template, filter state lives in the URL via history.pushState (a link with the chosen filters survives being forwarded to someone else), the mini cart runs on the Store API. Scroll animations sit on IntersectionObserver and respect prefers-reduced-motion. None of it is exotic. It is just ordinary modern frontend that the WordPress ecosystem, for whatever reason, still makes you assemble by hand.
There is a separate block-theme trap I ran into on the product page. The prototype called for a sticky info column on the right while the left photo column scrolled underneath it. position: sticky was right there in the CSS, and it flatly refused to work. The cause turned out to live in FSE itself: WordPress automatically wraps block content in containers carrying the is-layout-constrained class and its own styles, things like margin: auto, and those styles quietly break the context sticky positioning depends on. Everything looked fine in devtools too: the declaration was there, specificity checked out, and the element still scrolled along with the page instead of pinning. I dug it out with the same browser-first check from the workflow, looking at the computed styles on the parent wrappers instead of just my own component, and found an extra layout container FSE had inserted on its own. The lesson is narrow but concrete: in WordPress block themes, a sticky or absolute positioning bug is often not in your CSS at all, it is in an invisible wrapper WordPress put around the block.
Common failures: symptom, cause, fix
A condensed reference for the project's most frequent issues, for anyone who wants to check something fast instead of reading the full story.
Stock counts do not match the storefront, even though the exchange reports no errors.
Cause: CommerceML 3.1 stock arrives in a nested structure (<Остатки><Остаток><Склад>), not a flat field, and the plugin was expecting the old flat format.
Fix: a parser patch for the nested structure.
Check your own setup: open one rests_*.xml from an export and check whether <Количество> sits inside <Склад> or one level up. If it is nested, you have the same issue.
A product's size grid quietly shrinks with no visible cause. Cause: the fast exchange sends delta chunks, and the plugin rebuilt the whole variation set on every chunk, dropping anything not present in the current one. Fix: a mode that preserves variations on partial exports, plus a one-time cleanup of orphaned variations. Check your own setup: compare a product's variation count before and after a single delta export. Fewer variations afterward with no manual change is a match.
A size filter shows sizes that no in-stock product actually has. Cause: WooCommerce does not automatically recalculate attribute term counts after an import through the 1C plugin. Fix: force a term-count recalculation after every exchange. Check your own setup: after a sync, compare a term's stored count against the real number of in-stock products carrying it. A higher stored count is the tell.
A color swatch on some products stays the old shade after a site-wide background change. Cause: a design-token update never reached a hardcoded fallback value in PHP, used whenever a product has no color from 1C. Fix: pointed the fallback at the same token as the rest of the palette instead of a separate raw value. Check your own setup: find default or fallback color values living outside the main design system. Those are the ones that lag behind first when tokens change.
A menu quietly stops highlighting the right item after a content migration.
Cause: the logic was tied to a literal numeric menu-post ID instead of a registered theme location, and the ID shifted on export/import.
Fix: resolve through get_nav_menu_locations() instead of comparing against a literal.
Check your own setup: grep for comparisons against a bare number near menu or block logic. That is a candidate.
A sticky column on the product page does not stick, even though the CSS looks correct.
Cause: an automatic FSE wrapper (is-layout-constrained) with its own layout styles breaks the context position: sticky needs.
Fix: removed an extra layout wrapper around the sticky block.
Check your own setup: in devtools, inspect the computed styles on the parent wrappers, not just your own block. Look for a margin or display value you did not write.
The result
Here is what is actually running in production as of this writing.
A working store on a custom block theme that reproduces the design prototype one to one. A catalog with AJAX filters, variable product cards carrying sizes and colors, a wishlist, a product page with a swipe gallery on mobile.
Real-time sync with 1C:Retail: prices, stock, and variations arrive straight from the ERP with zero manual duplication. Since the stock-parser fix, the storefront finally matches the warehouse.
The local services layer is fully closed out: YooKassa and SBP payments, CDEK and postal delivery, privacy compliance handled.
Iteration speed says something too: two full rounds of client revisions, thirty items ranging from typography tweaks to block restructuring, each round cleared in a few days. Development time got measured in weeks, not months.
And the budget: zero dollars in licenses. WordPress, WooCommerce, the entire stack, all open source. The client's money went to labor and hosting, not to annual renewals on a boxed platform.
When WordPress + AI fits, and when it doesn't
This approach fits if you have:
- a small or mid-size store with a custom design;
- inventory in an ERP without a deep enterprise contour;
- a budget that genuinely matters;
- a developer willing to dig into exchange XML by hand instead of only clicking "install plugin."
And it does not fit, to be blunt about it, if you have:
- a high-load marketplace pushing thousands of orders a day;
- complex logistics with real-time per-warehouse reservations;
- a corporate requirement for vendor support contracts.
In those cases a boxed platform or a custom build on a different stack earns its price faster.
On the AI side, my conclusion is fairly plain, even if it is less flashy than the headline suggests. Claude Code does not replace a developer, it replaces part of a team. I would not have shipped a project this size alone on this timeline without the agent, or I would have shipped it on a genuinely different budget. But strip out the process, the rules in CLAUDE.md, the external memory, the mandatory browser verification, and the agent hands you confident, wrong code with a straight face. And that, honestly, is the real lesson of the whole project: building the process mattered more than learning to write clever prompts.
FAQ
How much does an online store on WordPress cost? Several times less than a build on a boxed commercial platform: no license fees, and AI-assisted development cuts down the manual hours. The exact number depends on the design, the integrations, and the size of the catalog, and gets quoted against a spec.
Can 1C be integrated with WooCommerce? Yes, over the CommerceML protocol, real-time stock and variable products included. The catch: the stock plugins need hand-finishing almost every time, see the 1C section above for specifics.
What is vibe coding in plain terms? Development where an AI agent writes the routine code while a human sets the tasks, designs the architecture, and reviews the results. Speed climbs several-fold; quality holds up only as long as the review discipline does.
Does Claude Code replace a developer? No. It covers part of a team: a junior frontend developer, a tester handling routine checks, a technical writer. Architecture, task decomposition, and final acceptance stay with the human.
How do I deploy a WooCommerce store with Claude Code? Connect the agent to your hosting through an SSH MCP server and to a browser through Playwright, put your rules in CLAUDE.md, and require a check on the live page after every deploy. The step-by-step version is in the deploy section above.
Can Claude Code connect to WooCommerce directly? On this project it did not need to: the agent worked with the theme code, the server over SSH, and the live store in a browser, with no WooCommerce-specific connector in the tooling.
Does WooCommerce work with YooKassa and SBP? Yes. YooKassa ships an official WooCommerce module covering card payments and SBP.