AMODX Sprint 5: Physical Goods Commerce Engine
By Andrei Roman
Principal Architect, The Foundry
Sprint 3 added digital goods (Paddle integration, gated content). Sprint 4 added visual polish (Coverflow, Content Graph, Versioning). Sprint 5 added physical goods commerce. Complete WooCommerce feature parity plus domain-specific requirements for a Romanian bakery client. Not generic e-commerce. Domain-specific business logic that broke every assumption. Here is what happened.
The Client Problem
An established bakery running on WooCommerce.
They wanted security. But they needed everything WooCommerce gave them plus custom features WooCommerce could not do without expensive plugin combinations or custom development.
The requirements list was brutal. Minimum order threshold. Free delivery threshold. Product availability by date (seasonal cookies). Delivery date picker that respects weekly off days, yearly holidays, and per-month exceptions. Personalization strings with cost adders (custom text on cookies costs extra). Bulk pricing with multiple quantity tiers. Two separate product descriptions (marketing copy and legal ingredients/nutrition). Google My Business review auto-sync.
Plus the WooCommerce admin basics: products with variations, orders with status workflow and email notifications, customer management, coupons, reports.
Plus legal requirements: company registration details in footer, links to consumer protection agencies, GDPR compliance, cookie banners, privacy policies.
This is why agencies charge top dollar for WooCommerce sites. The features stack. The edge cases multiply. The client has a real business with real constraints that do not fit generic e-commerce templates.
The Hurdles
Slug Conflicts
Content pages use slugs. Products use slugs. Categories use slugs. What happens when the client creates an About page and an About product category?
WordPress handles this with taxonomy prefixes baked in (product-category, product-tag). AMODX had to solve it at the routing layer. Content gets /page/about, /post/news, /category/tech. Commerce gets /product/cookie-box, /shop/category/cookies, /cart, /checkout. Namespace prefixes. Explicit routing. No collisions.
Baked in preventing conflicts at 2 stages - when specifying the commerce slugs the system checks that they are not already used - and when creating a new page the system checks that the slug is not used by the commerce components.
Delivery Date Picker
Client requirement: customers select a delivery date at checkout. Bakery needs three days lead time, doesn't work Sundays or Mondays, closes for Romanian National Day (December 1) and Christmas (December 25), but sometimes opens on normally-closed days (e.g. Monday December 23).
Configuration has four layers: weekly off days (weekday numbers), yearly recurring holidays (MM-DD strings), blocked dates (specific YYYY-MM-DD dates forced unavailable), and unblocked dates (specific YYYY-MM-DD dates forced available despite other rules). A 5-level priority resolves conflicts: unblocked dates override blocked dates, which override yearly holidays, which override the weekly schedule.
Lead time calculation skips off days — three lead days means three working days, not calendar days. The admin UI includes a 2-month visual calendar with color-coded days where clicking toggles overrides. The checkout shows an inline date picker that fetches available dates from the API and disables all unavailable days.
Personalization Cost Adder Breaks Bulk Pricing
Bulk pricing: 10 cookies for €1 each. 20 cookies for €0.90 each. 50 cookies for €0.80 each.
Personalization: Add custom text for €0.50 per cookie.
Customer orders 50 personalized cookies. What is the price?
Option A: 50 × €0.80 = €40 (bulk price), then add 50 × €0.50 = €25 (personalization). Total €65.
Option B: Base price €1 + personalization €0.50 = €1.50 per cookie. 50 × €1.50 = €75. No bulk discount because personalization changes the product.
We do Option A.
Local Payment Methods
No Stripe. No PayPal. In some markets, customers prefer bank transfer or cash on delivery.
Bank transfer: Customer places order, receives email with bank account details and order reference number. Customer transfers money. Bakery checks bank account manually. Bakery updates order status to Processed. No automation. No webhooks. Manual reconciliation.
Cash on delivery: Courier collects payment. Bakery receives cash from courier later. Same manual reconciliation problem.
Built order status workflow with six states: Placed, Processed, Prepared, Delivered, Cancelled, Annulled. Admin manually advances orders through states. Email notifications trigger on state changes. Not elegant, but it works.
Google My Business API
Client wanted reviews from Google My Business displayed on product pages. Google has API. Should be easy.
Wrong:
- OAuth 2.0 required — no API keys, must get user consent
- App verification by Google — takes weeks, they can reject you
- Access tokens expire hourly, refresh tokens can be revoked
- API quota is tight — was historically very low for unverified apps
- API has been renamed/restructured multiple times (Google Places API, Google My Business API, Google Business
Profile API — different things)
- Location must be claimed and verified by the business owner
- Reviews endpoint is read-only and sometimes returns partial data
What exists:
- ReviewSchema has source: google and googleReviewId field
- Admin Reviews page where you can manually create/moderate reviews
- Public reviews API that displays them on product pages
What doesn't exist:
- No Google API OAuth flow
- No token storage
- No EventBridge Lambda
- No automated sync
So we have the manual path only - which is honestly the right call for most small clients.
Infrastructure Limits
The CDK stack hit CloudFormation's 500-resource limit at 710 resources after adding commerce and engagement Lambda functions. Each Lambda generates ~5 CloudFormation resources (function, role, policy, log group, permission), so 90+ Lambdas exhausted the limit.
Solution: split into a parent stack and 2 NestedStacks. The parent stack (~390 resources) contains all originally deployed Lambdas — content, settings, auth, assets, audit, products admin, users, webhooks, signals, research, themes. The CommerceStack NestedStack (~234 resources) contains categories, public products, orders, customers, delivery, coupons, reviews, and WooCommerce import — 33 Lambdas total. The EngagementStack NestedStack (~94 resources) contains popups and forms — 13 Lambdas.
NestedStacks deploy atomically with the parent via a single cdk deploy command. If any nested stack fails, the entire deployment rolls back. No CI/CD changes needed, no sequential deployment, no partial failure risk.
WooCommerce Import
The import endpoint accepts a CSV file upload and maps WooCommerce export columns to the AMODX ProductSchema. It handles simple products, variable products, and their variations using a two-pass algorithm.
Pass 1 separates rows by product type: simple and variable rows become parent products, variation rows are grouped by their parent slug. Pass 2 processes each parent — if it has variations, the importer extracts attribute dimensions (e.g. "Weight" with values "250g", "500g") from the variation rows and builds variant groups with per-option price overrides, images, and availability.
The importer auto-creates categories from WooCommerce's "Parent > Child" hierarchical format, maps stock status strings (instock, outofstock, onbackorder) to the availability enum, and preserves original WooCommerce slugs for SEO continuity.
The admin UI is a file picker button on the Products page. Select a CSV, it uploads and processes in one request, returns a count of created products and categories. There is no preview step, no progress bar, and no per-row error display — if a row fails validation it's silently skipped. The endpoint is designed for one-time migration use, not repeated bulk operations.
What Shipped
Physical goods commerce engine. Products with variations (size, flavor, packaging). Categories and tags. Bulk pricing (quantity-based tiered discounts). Personalization fields (custom text, dropdown options, each with per-unit cost adder). Interactive delivery date picker (weekly off days, yearly recurring holidays, specific date overrides, lead time calculated across working days only). Orders with seven-state workflow (Placed, Confirmed, Prepared, Shipped, Delivered, Cancelled, Annulled). Configurable email templates per status with variable substitution, selectable recipients (customer, admin, processing). Coupons (percentage, fixed amount, free delivery) with server-side validation and atomic usage tracking.
Admin panel. Product editor with variants tab, SKU, bulk pricing tiers, personalizations, availability date window, image gallery. Order management (view, update status, email customer with templated notifications).
Coupon editor with usage limits and expiry. Customer list with order history. Delivery settings with visual 2-month calendar for managing off days and overrides. Email template editor per order status. Activity logs (admin actions, form submissions, import results). WooCommerce CSV import with two-pass variable-product parsing, auto-category creation, and error logging to audit trail.
Frontend components. Commerce navbar with shrink-on-scroll and quick contact bar (phone, WhatsApp, social icons, CTA button). Product pages with interactive image gallery (clickable thumbnails, navigation arrows, image counter), variation selector, personalization inputs, volume pricing display, and customer reviews with aggregate star rating. Cart with free delivery progress bar and coupon input. Checkout with customer form, inline delivery date picker, configurable payment methods (cash on delivery, bank transfer), and order summary. Order confirmation with bank transfer payment instructions. Order tracking page with status timeline.
Content editor plugins (17 custom blocks). Hero, pricing table, image, contact form, video embed, lead magnet, CTA, feature grid, testimonials, columns, data table, raw HTML, FAQ accordion (with JSON-LD), post grid, carousel, code block, and reviews carousel (Google/Facebook badges, star ratings, horizontal scroll with auto-scroll option).
Dynamic forms builder. Custom field types (text, email, textarea, select, checkbox). Configurable slugs for embedding in pages. Admin submission viewer.
Popup system. Trigger on page load, exit intent, scroll depth, or timed delay. Display targeting (all pages, specific pages). Frequency control.
Facebook Pixel integration. Tracks PageView, AddToCart, InitiateCheckout, and Purchase events. Pixel only loads when cookie consent is granted.
Legal compliance. Multi-column footer with company details (legal name, CUI fiscal code, trade register number, address). Links to ANPC consumer protection and SOL online dispute resolution. Configurable legal page links (terms, privacy, cookies). GDPR cookie consent banner.
Reviews. Manual entry through admin panel with star rating, author name, and optional Google source attribution. Displayed on product pages with aggregate rating in structured data (JSON-LD) for SEO. Reviews carousel block for embedding curated reviews on any page.
Why This Matters
AMODX built all of the WooCommerce requirements into core. No plugins to update. No version conflicts. No security patches every week. Client owns infrastructure (deployed to their AWS account). Zero monthly SaaS fees. Serverless (Lambda + DynamoDB + CloudFront). Zero idle cost when site not busy.
Multi-tenant by design. One deployment handles multiple bakeries, shops, merchants. Agency operating system. Deploy once, serve many clients. Per-tenant commerce toggle. Shared infrastructure amortized across clients.
This is why agencies need infrastructure ownership, not WordPress hosting.
What Deferred to Sprint 6
Reports page (order totals, revenue by period, top products). The order data exists in DynamoDB — this is a read-only admin page querying existing records. No new infrastructure needed. Worth building when the client has enough order volume to make dashboards useful.
Customer accounts. Schema and URL prefix are ready. Implementation needs: Google OAuth login in site header, account page with order history and saved address, checkout pre-fill from session. Enhances repeat customer experience.
Form email notifications. The forms system saves submissions but doesn't email the configured notifyEmail address. Needs SES integration matching the existing contact form pattern. Small effort.
Courier tracking integration. Would require contracts with Romanian couriers (Fan Courier, Cargus, Sameday) and their APIs. Deferred until a client actively ships enough volume to justify it.
Agency tooling (automated WooCommerce migration, white-label admin, bulk tenant provisioning). The WooCommerce CSV import exists. White-label and bulk provisioning would reduce onboarding time but require dedicated UI work. Deferred until agency has 5+ active commerce tenants.
Discussion (0)
No comments yet. Be the first!