WooCommerce ERP Integration Architecture: Webhooks, Rate Limits & Data Synchronization

Architektur-Blueprint: So integrieren Sie ein ERP System mit WooCommerce
Die stabilste Architektur für Data Synchronization zwischen WooCommerce und einem ERP System besteht aus (1) Event-getriebener Datenaufnahme via Webhook Architecture, (2) entkoppelter Verarbeitung über Queue/Worker und (3) bidirektionalen API-Flows über die WooCommerce REST API. Ziel: konsistente Stammdaten, saubere Zustandsmodelle und kontrollierte Last (Stichwort: API Rate Limits).
Interessiert an diesem Thema?
Kontaktieren Sie uns für eine kostenlose Beratung →1) Domänen & Datenhoheit definieren (Source of Truth)
- ERP System als Source of Truth für: Preise, Lagerbestand, Steuerlogik, Kundensegmente, Rechnungen, Lieferscheine.
- WooCommerce als Source of Truth für: Warenkorb/Checkout, Payment-Status, Shop-spezifische Produktdarstellung, Shop-Coupons (optional).
- Mapping-Entitäten: product/variant (SKU), customer, order, stock item, shipment, invoice.
Wichtig: Legen Sie pro Entität ein eindeutiges Mapping fest (z. B. SKU für Produkte, ERP Customer ID in WooCommerce user meta, ERP Order ID in order meta). Ohne stabile Schlüssel eskalieren Dubletten und Konflikte.
2) Event-Flows festlegen: Was ist inbound, was outbound?
- WooCommerce → Integration → ERP: neue Bestellungen, Statuswechsel (paid/processing/completed), Rückerstattungen, neue Kunden.
- ERP → Integration → WooCommerce: Lagerbestände, Preise, Produktstammdaten, Versand-/Trackingdaten, Rechnungsstatus.
Die Integration-Schicht (Middleware) ist Ihr „Vertrag“ zwischen Shop und ERP: sie übersetzt Datenmodelle, kapselt Retries, überwacht Dead Letters und schützt beide Systeme vor Lastspitzen.
Webhook Architecture: Ereignisse sicher aufnehmen & verarbeiten
3) Webhooks als Trigger (nicht als Verarbeitung)
Webhooks sollten nur ein Ereignis signalisieren (z. B. „order.created“) und die Verarbeitung an eine Queue delegieren. Das verhindert Timeouts und macht Ihre Data Synchronization resilient.
- Webhook Receiver (öffentlich erreichbar): validiert Signatur/Secret, normalisiert Payload, erzeugt Idempotency-Key, enqueued Job.
- Queue (z. B. SQS/RabbitMQ/Redis): puffert Last und entkoppelt ERP-Verfügbarkeit.
- Worker: lädt bei Bedarf Details via WooCommerce REST API, mappt und schreibt ins ERP System.
4) Idempotenz & Duplikatschutz
Webhooks können mehrfach kommen (Retries, Netzwerk, Plugin-Resends). Nutzen Sie:
- Idempotency Key pro Event: z. B.
eventType:resourceId:timestampBucketoder WooCommerce webhook delivery ID (falls verfügbar). - Outbox/Inbox Pattern: speichern Sie empfangene Events mit Status (received/processing/done/failed).
- Upserts im ERP: Order-Update statt Order-Insert.
5) Fehlertoleranz: Retry, Backoff, Dead Letter Queue
- Retry mit Exponential Backoff für temporäre Fehler (429, 503, Timeouts).
- Dead Letter Queue für dauerhaft fehlerhafte Jobs (Schema-Fehler, Mapping-Lücken).
- Replay aus DLQ nach Fix (Mapping ergänzt, Bug behoben).
API Rate Limits: WooCommerce REST API ohne Drosselung nutzen
6) Rate-Limit-Strategie (Client-seitig)
Auch wenn WooCommerce/Hosting-Setups Rate-Limits unterschiedlich durchsetzen (z. B. via WAF/CDN, PHP-FPM Limits, REST API Throttling-Plugins), müssen Sie client-seitig defensiv planen:
- Concurrency Limits: begrenzen Sie parallele Requests (z. B. 3–10).
- Backoff bei 429: warten und erneut versuchen;
Retry-Afterrespektieren, falls gesetzt. - Batching/Pagination: Produkte/Bestände in Seiten laden und delta-basiert aktualisieren.
- Delta Sync: statt Full Sync nur Änderungen seit
lastSyncAtübertragen.
7) Rate-Limits serverseitig entschärfen
- Webhooks statt Polling für Orders/Kunden (reduziert API Calls massiv).
- Read-Models: in Ihrer Middleware cachen (z. B. Produkt-Mapping SKU → product_id).
- Scheduling: schwere Jobs (Full Catalog Sync) in Nebenzeiten.
Referenz-Architektur (Diagramm)
flowchart LR
A[WooCommerce] -- Webhooks --> B[Webhook Receiver]
B --> C[(Inbox Store / Idempotency)]
B --> D[[Queue]]
D --> E[Worker / Mapper]
E -- REST: fetch details --> A
E -- Create/Update --> F[ERP System]
F -- Changes (stock/price) --> G[ERP Export Job]
G --> H[Woo REST API Client]
H --> A
E --> I[(Logs/Tracing/Metrics)]
G --> I
Datenmodell & Synchronisations-Entscheidungen (Tabelle)
| Feature | Details |
|---|---|
| Order Sync (Woo → ERP) | Webhook order.created/order.updated → Queue → Worker lädt Order-Details via WooCommerce REST API → ERP Upsert mit Idempotenz |
| Stock Sync (ERP → Woo) | Delta-Export aus ERP (nur geänderte SKUs) → Woo REST API Update, Concurrency + Backoff (API Rate Limits) |
| Produkt-Mapping | SKU als Primärschlüssel; lokales Mapping Cache: SKU ↔ Woo product_id/variation_id ↔ ERP item_id |
| Konfliktregeln | Preis/Lager: ERP gewinnt; Status/Payment: Woo gewinnt; mit Zeitstempel- und Statusmatrix |
| Observability | Korrelations-ID pro Job, strukturierte Logs, DLQ, Metriken für 429/5xx, Sync-Latenz pro Entität |
Implementierungsbausteine
8) TypeScript Service für WooCommerce REST API Integration (Rate-Limit aware)
Der Service kapselt Auth, Retries (inkl. 429), Backoff, Pagination und stellt Methoden bereit, um Orders abzurufen und Bestände zu aktualisieren. In der Praxis wird er vom Worker genutzt, der Jobs aus der Queue verarbeitet und mit dem ERP System kommuniziert.
**TypeScript Service for API Integration**import crypto from "crypto";
export type WooConfig = {
baseUrl: string;
consumerKey: string;
consumerSecret: string;
apiVersion?: "wc/v3";
timeoutMs?: number;
maxRetries?: number;
concurrency?: number;
};
export type WooOrder = {
id: number;
status: string;
number: string;
date_created_gmt: string;
currency: string;
total: string;
billing?: {
email?: string;
first_name?: string;
last_name?: string;
};
line_items: Array<{
id: number;
product_id: number;
variation_id: number;
sku?: string;
quantity: number;
total: string;
}>;
meta_data?: Array<{ key: string; value: unknown }>;
};
export type StockUpdate = {
productId: number;
variationId?: number;
stockQuantity: number;
manageStock?: boolean;
};
class HttpError extends Error {
public status: number;
public body: string;
public headers: Headers;
constructor(message: string, status: number, body: string, headers: Headers) {
super(message);
this.status = status;
this.body = body;
this.headers = headers;
}
}
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
export class WooCommerceClient {
private cfg: Required<WooConfig>;
constructor(cfg: WooConfig) {
this.cfg = {
apiVersion: cfg.apiVersion ?? "wc/v3",
timeoutMs: cfg.timeoutMs ?? 20_000,
maxRetries: cfg.maxRetries ?? 6,
concurrency: cfg.concurrency ?? 5,
...cfg,
} as Required<WooConfig>;
}
private authHeader(): string {
const token = Buffer.from(
`${this.cfg.consumerKey}:${this.cfg.consumerSecret}`,
"utf8"
).toString("base64");
return `Basic ${token}`;
}
private async request<T>(method: string, path: string, body?: unknown): Promise<T> {
const url = new URL(`${this.cfg.baseUrl.replace(/\/$/, "")}/wp-json/${this.cfg.apiVersion}${path}`);
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), this.cfg.timeoutMs);
try {
const res = await fetch(url.toString(), {
method,
headers: {
Authorization: this.authHeader(),
"Content-Type": "application/json",
Accept: "application/json",
},
body: body ? JSON.stringify(body) : undefined,
signal: controller.signal,
});
const text = await res.text();
if (!res.ok) {
throw new HttpError(`Woo request failed: ${method} ${path}`, res.status, text, res.headers);
}
return text ? (JSON.parse(text) as T) : (undefined as T);
} finally {
clearTimeout(timeout);
}
}
private async requestWithRetry<T>(method: string, path: string, body?: unknown): Promise<T> {
let attempt = 0;
while (true) {
try {
return await this.request<T>(method, path, body);
} catch (err) {
attempt += 1;
const httpErr = err instanceof HttpError ? err : undefined;
const status = httpErr?.status;
const retryable =
status === 429 ||
status === 408 ||
(status !== undefined && status >= 500 && status <= 599);
if (!retryable || attempt > this.cfg.maxRetries) {
throw err;
}
const retryAfterHeader = httpErr?.headers.get("retry-after");
const retryAfterMs = retryAfterHeader ? Number(retryAfterHeader) * 1000 : NaN;
const baseBackoff = Math.min(30_000, 500 * Math.pow(2, attempt));
const jitter = crypto.randomInt(0, 250);
const waitMs = Number.isFinite(retryAfterMs) ? retryAfterMs : baseBackoff + jitter;
await sleep(waitMs);
}
}
}
public async getOrder(orderId: number): Promise<WooOrder> {
return this.requestWithRetry<WooOrder>("GET", `/orders/${orderId}`);
}
public async listOrders(params: {
modifiedAfterGmt?: string;
page?: number;
perPage?: number;
status?: string;
}): Promise<WooOrder[]> {
const qp = new URLSearchParams();
qp.set("per_page", String(params.perPage ?? 50));
qp.set("page", String(params.page ?? 1));
if (params.status) qp.set("status", params.status);
if (params.modifiedAfterGmt) qp.set("modified_after", params.modifiedAfterGmt);
return this.requestWithRetry<WooOrder[]>("GET", `/orders?${qp.toString()}`);
}
public async updateStock(update: StockUpdate): Promise<unknown> {
const isVariation = typeof update.variationId === "number" && update.variationId > 0;
const payload = {
manage_stock: update.manageStock ?? true,
stock_quantity: update.stockQuantity,
};
if (isVariation) {
return this.requestWithRetry(
"PUT",
`/products/${update.productId}/variations/${update.variationId}`,
payload
);
}
return this.requestWithRetry("PUT", `/products/${update.productId}`, payload);
}
}
9) JSON Config für Webhooks (Topics, Delivery URL, Secret)
Die folgende Konfiguration ist als Beispiel gedacht (z. B. für Ihre interne Deployment-Doku oder ein Provisioning-Script). Sie bildet die „minimal notwendigen“ Events ab, um Orders zuverlässig ins ERP zu synchronisieren.
**JSON Config for Webhooks**{
"webhooks": [
{
"name": "ERP Sync - Order Created",
"status": "active",
"topic": "order.created",
"delivery_url": "https://integration.example.com/webhooks/woocommerce/order-created",
"secret": "REPLACE_WITH_LONG_RANDOM_SECRET",
"api_version": "wc/v3"
},
{
"name": "ERP Sync - Order Updated",
"status": "active",
"topic": "order.updated",
"delivery_url": "https://integration.example.com/webhooks/woocommerce/order-updated",
"secret": "REPLACE_WITH_LONG_RANDOM_SECRET",
"api_version": "wc/v3"
},
{
"name": "ERP Sync - Refund Created",
"status": "active",
"topic": "refund.created",
"delivery_url": "https://integration.example.com/webhooks/woocommerce/refund-created",
"secret": "REPLACE_WITH_LONG_RANDOM_SECRET",
"api_version": "wc/v3"
}
],
"receiver": {
"verifySignature": true,
"idempotency": {
"store": "postgres",
"ttlHours": 168
},
"queue": {
"provider": "sqs",
"name": "woo-events",
"deadLetterQueue": "woo-events-dlq"
}
}
}
Betrieb: Monitoring, Sicherheit, Compliance
10) Monitoring & Alerting
- Sync-Lag: Zeit zwischen Woo-Order und ERP-Order.
- Fehlerraten: 429/5xx der WooCommerce REST API, ERP-Fehlercodes, DLQ-Füllstand.
- Audit Trail: Wer/was hat welchen Status/Bestand geändert (inkl. Korrelation zu Webhook-Event).
11) Sicherheit (Webhook & API)
- Webhook Secret nutzen und serverseitig verifizieren; Requests ohne gültige Signatur verwerfen.
- Allowlist (wenn möglich): nur Woo/Web-Application-Firewall IP-Ranges zulassen (abhängig vom Hosting).
- Least Privilege: getrennte API Keys pro Umgebung (dev/stage/prod), Rotation und Secrets-Management.
Wann lohnt sich professionelle Unterstützung?
Wenn Sie viele SKUs, Varianten, Multi-Warehouse-Bestände oder komplexe Fulfillment-Prozesse haben, zahlt sich eine saubere Integrationsarchitektur schnell aus (weniger Bestandsfehler, weniger manuelle Korrekturen, bessere Skalierung). Für Planung, Umsetzung und Betrieb einer belastbaren Integration unterstützt Sie eine WooCommerce Agentur.


