Code by

Carter

Phan

Omnichannel E-commerce Platform

Omnichannel E-commerce Platform

Year2025
WooCommerceMySQLRedisDockerNginxWebhook Retry & IdempotencyEvent-driven Architecture

Project Description

A centralized omnichannel commerce system that synchronizes inventory across multiple marketplaces, designed to survive unreliable webhooks, high traffic, and real-world failure scenarios.

Have you ever tried to keep inventory accurate when the exact same product is being sold across multiple platforms simultaneously? That deceptively simple question is what kicked off this entire project.

My goal was to integrate WooCommerce with massive external sales channels like Shopee, Lazada, and TikTok Shop. But I quickly realized that treating each channel as a completely separate system was a recipe for disaster. Instead, I decided to treat inventory as a shared resource, establishing WooCommerce as the central source of truth.

High-level architecture diagram. Show Shopee, Lazada, and TikTok Shop nodes pointing inward to a central WooCommerce hub

The Real Problem: Surviving Third-Party Platforms

If you've ever worked with marketplace APIs, you know that the real challenge isn't building the initial integration. The true test is designing a system that can survive the absolute chaos of third-party platforms.

Every marketplace behaves differently, and honestly, none of them are reliable in the exact same way. Here is a glimpse of what I had to deal with:

  • Some platforms aggressively retry webhooks.
  • Others send duplicate events with tiny, maddening payload differences.
  • Sometimes events are delivered completely out of order, or painfully late.
  • Worse yet, some platforms will time out on your end but still process the order on theirs anyway!

If my system let every incoming webhook immediately mutate the stock, overselling during a flash sale wouldn't just be a risk — it would be an inevitability. The core problem became clear: I had to learn to trust absolutely nothing, while still allowing the system to process orders and move forward.

Expectation flowchart showing a neat, orderly line of webhooks vs a Reality graphic showing webhooks crashing into the server out of order

Event-First Design & The Safety Net of Idempotency

To solve this, I shifted my primary goal. This project wasn't about speed; it was entirely about correctness. Rather than chasing the dangerous illusion of instant updates, I designed the system to eventually converge to the correct state, even under failure conditions.

One of the best decisions I made was treating webhooks strictly as events, not commands. When a webhook hits the server, the plugin does the absolute minimum:

  • It validates the request.
  • It persists the raw event to the database.
  • It responds to the marketplace immediately.

Absolutely no inventory logic runs at this stage. Instead, all the heavy lifting and business logic is handled asynchronously through background jobs. This guarantees that webhook delivery is never blocked, and if a processing failure happens, the original event is never lost.

Because I knew duplicate events were expected (not exceptions), idempotency became my safety net. Each incoming webhook generates a unique idempotency key derived from the sales channel, the event type, and the external order ID. Before a background job processes anything, the system checks whether that specific event has already been handled. If it has? The job just exits safely without causing any side effects. That single design choice wiped out an entire class of nasty race conditions.

Idempotency key flowchart showing the "Has this been processed?" decision tree

The Reservation Model: Preventing the Oversell

Instead of directly decrementing stock the moment an incoming order event arrives, I implemented a reservation-based approach.

Here is how it works:

  • Inventory is temporarily reserved when an order is created.
  • It is only finalized when the order reaches a confirmed state.
  • If the order expires or is canceled, the reservation is simply released back into the pool.

This approach was a game-changer. It dramatically reduced database contention during high-traffic flash sales and made the stock behavior entirely predictable under heavy load.

Building a System That Heals Itself

Failures are going to happen, so I decided to treat them as normal behavior. The system is built to heal itself without manual intervention:

  • Transient failures automatically trigger retries using a backoff strategy.
  • Hard failures are isolated for inspection rather than blocking the entire queue.
  • Timeouts do not cause duplicate inventory mutations.

Furthermore, each sales channel operates independently. If TikTok Shop experiences an outage or starts sending malformed events, Shopee and Lazada continue to function normally. The system degrades gracefully rather than collapsing entirely, preserving inventory consistency across the board.

Doing It All in WordPress

You might be wondering about the stack. I built this as a custom WordPress / WooCommerce plugin, fully integrated into the existing ecosystem. The stack relies on PHP 8.x, MySQL, Redis for caching and locks, and background jobs for async processing, all running in a Dockerized environment.

Staying within WordPress was a very intentional decision. The real intellectual challenge was figuring out how to build robust, asynchronous, backend behavior inside what is traditionally a synchronous environment.

What This Project Taught Me

This project fundamentally reshaped how I think about backend systems, especially when working inside constrained environments like WordPress.

It reinforced a few hard truths for me:

  • Consistency is a spectrum, not a binary state.
  • Defensive design is not pessimism — it’s realism.
  • Reliable systems are often incredibly boring by design.
  • Asynchronous thinking matters, even in synchronous platforms.

Most importantly, it taught me that good systems respect reality instead of trying to fight it.

What’s Next?

If I were to continue evolving this project, I have a few improvements in mind. I’d focus on event schema versioning for safer long-term evolution, better replay tooling for historical webhook events, more granular operational dashboards, and chaos testing to simulate extreme failure scenarios.