Practical guide
How to connect a shop to a warehouse or ERP system
Assign an owner to every data point and build an integration that survives duplicates, outages, and late changes.
In short
Synchronization needs authority, not the last writer
First decide whether the shop, ERP, or another system owns each price, stock level, product, order, and document. Bidirectional last-write-wins silently overwrites correct data.
The integration is a separate application boundary. It translates the internal model into a versioned API contract, maintains ID mappings, and records every synchronization operation.
Prepare
Write a data contract
A mapping table exposes differences in units, states, and responsibility before the first production import.
- A source-of-truth matrix for entities and important fields, including synchronization direction.
- Stable internal and external IDs, units, currencies, time zones, enum values, and null rules.
- An ERP sandbox or test account with realistic limits, pagination, and failure responses.
- An operations owner, data-freshness metrics, alerts, and a manual conflict-resolution procedure.
Steps 1 to 3
Design synchronization as a repeatable process
Every step can run again. Success is determined from the persisted result, not from whether a request was sent once.
1. Define data ownership and identity
- Choose the authoritative system independently for products, prices, inventory, customers, orders, shipments, and documents.
- Create internal_id ↔ system ↔ external_id mappings with a unique constraint. Do not match long-term by a name or mutable SKU.
- Separate physical stock, reservations, and available to sell. Never send one stock number without defining its meaning.
- Keep a snapshot of price, tax, address, and line items on an order; later ERP catalogue changes must not rewrite history.
UNIQUE (external_system, external_id) Official PostgreSQL constraints documentation 2. Stabilize the API contract and transport
- Version and schema the contract: required fields, decimals, ISO 8601 times, pagination, deletion, and state meanings.
- Read a large initial import in batches. Then transfer changes with events or an updated_at watermark plus periodic full comparison.
- Write outbound orders to a transactional outbox. Verify webhook signatures, time windows, and event IDs.
- Set short timeouts, rate limits, exponential retry with jitter, and a correlation ID visible on both sides.
GET /products?updated_after=...&cursor=... HTTP Semantics — RFC 9110 3. Add idempotency and reconciliation
- Process each imported record by stable ID and version. An older change must not overwrite newer state.
- Send an idempotency key when exporting an order and persist the external response ID. A timeout does not prove the ERP did nothing.
- A periodic reconciliation job compares counts, checksums, or changed entities and repairs events lost on either side.
- Quarantine invalid data with a reason. One bad item must not block an entire synchronization batch.
php bin/console app:erp:reconcile --since="-1 day" Official Symfony HttpClient documentation Step 4
Verify the integration boundaries
Alongside a correct payload, test ordering, duplicates, partial failures, and a multi-hour outage.
-
Run a contract test
ERP samples pass schema validation and mappings for units, currencies, times, and enums.
php bin/phpunit --testsuite=erp-contract -
Deliver changes out of order and twice
The resulting product, stock, and order match the newest version without duplicates.
-
Disconnect and reconnect the ERP
The queue stays bounded, retries do not overwhelm the service, and reconciliation fills every gap.
Troubleshooting
Common problems
Duplicate products appear
Matching relies on a mutable SKU or name. Maintain a stable mapping table and database unique constraint.
Inventory regularly jumps back
Both systems consider themselves authoritative. Assign stock ownership and separate physical stock from shop reservations.
A timeout created an ERP order twice
The retry has no idempotency key. Use the stable order key and check the saved external ID before creating.
Synchronization is green but data is stale
You only measure process execution. Track the lag of the last confirmed change and reconcile regularly.
Done
The integration has clear authority and a repair path.
Stable identities, a versioned contract, idempotent transport, and regular comparison keep the shop and ERP consistent through outages.