Most order integrations work on the first day. A test order is placed, it shows up in the accounting system, everyone is happy. The problems arrive later, on the day the accounting system is slow, the warehouse API returns an error or the store sends the same order twice.
That is when an integration either quietly recovers or produces duplicate invoices, missing shipments and someone typing orders in by hand. Which of the two you get depends on how the integration handles failures.
Push or pull
There are two ways to get orders from a store into another system.
Webhooks (push). The store sends a request to a URL as soon as something happens, such as an order being created or paid. WooCommerce has built-in webhooks, configured under WooCommerce > Settings > Advanced > Webhooks as described in the webhooks documentation.
Polling (pull). The receiving side asks the store at intervals: give me all orders changed since the last time I asked.
Webhooks are fast and cheap, but they can be lost. If the receiver is down when the webhook is sent, the event does not arrive. WooCommerce does not retry a failed delivery, and after repeated consecutive failures it disables the webhook. The REST API documentation describes the default limit as five, and it can be changed with the woocommerce_max_webhook_delivery_failures filter. Nobody is notified when a webhook is disabled.
Polling is slower but self-correcting. If a run fails, the next run picks up everything since the last successful one.
The reliable setup is usually both. Use webhooks for speed, and a regular polling job that catches anything the webhooks missed.
The same order will arrive twice
Any system that retries will sooner or later deliver the same thing more than once. A webhook times out on the sender's side but was in fact processed. A job crashes after sending an invoice but before recording that it did. Stripe says this outright in its webhook documentation: events may be delivered more than once, and not always in order.
So the receiving side has to be idempotent: processing the same order twice must have the same result as processing it once.
In practice:
- Use a stable identifier from the store, such as the order ID, as the key in the other system.
- Before creating an invoice or a shipment, check whether one already exists for that order.
- Record what has been sent, and treat a repeated request for the same order as an update, not as a new order.
Many APIs support this directly through idempotency keys. Stripe's idempotent requests are a good example: the client sends a unique key with the request, and if the same key is reused within 24 hours, Stripe returns the saved response instead of performing the action again. HTTP itself defines which methods are idempotent in RFC 9110, which is a useful reference when designing your own endpoints.
When the other system is down
Accounting systems have maintenance windows, warehouse APIs have bad days and rate limits get hit during sales. An integration that sends orders synchronously, during checkout, turns every one of those into a problem for the customer.
The safer pattern:
- Save the order in the store as usual.
- Put "send order 1234 to accounting" in a queue.
- A background worker sends it and retries with increasing delays if it fails.
- After a number of failed attempts, the job is marked as failed and someone is told.
WooCommerce already has a queue built in, Action Scheduler, which is a reasonable place for this on most stores. The customer never waits for the accounting system, and a short outage turns into a short delay instead of a lost order.
Verify what arrives
Anyone who knows the URL can send a request to a webhook endpoint. WooCommerce signs each webhook with a secret: the X-WC-Webhook-Signature header contains a base64-encoded HMAC-SHA256 hash of the request body, made with the webhook's secret. The receiver should compute the same hash with the shared secret and reject the request if it does not match.
The same goes for API keys in the other direction. Give the integration its own key with only the permissions it needs, and not an administrator account.
Decide which system owns what
Double entry often comes from two systems that both think they own the same data. Stock is changed in the store and in the warehouse system. A customer address is corrected in accounting but not in the store.
Before building anything, write down for each kind of data which system is the source of truth:
- Orders: usually the store.
- Stock levels: usually the warehouse.
- Prices: whichever system the business edits them in.
- Invoices and payments: the accounting system.
Data flows from the owner to everyone else. When two systems are allowed to change the same field, they will eventually disagree, and syncing cannot resolve which one is right.
Updates, refunds and cancellations
New orders are the easy part. An order also changes after it is placed: the customer cancels, an item is refunded, the address is corrected, a payment fails and is retried.
Each of those has to reach the other system too, and in the right form:
- A cancellation should cancel the shipment in the warehouse, if it has not left yet, and produce a credit note in accounting if an invoice was already created.
- A partial refund is a new document in accounting, not an edit of the original invoice.
- A changed address matters to the warehouse until the parcel is packed, and not after.
Integrations that only handle "order created" leave all of this to someone doing it by hand, which is exactly the double entry the integration was meant to remove. List the events that matter before building, and decide what each one should do in each system.
Order also matters here. If a refund arrives before the order it belongs to, because a webhook was delayed, the receiver needs to wait or fetch the order first rather than fail.
Make failures visible
The worst integrations fail silently. Orders stop arriving, and someone notices at the end of the month when the numbers do not match.
Four things make problems visible early:
- A log of every order sent, with the response from the other system.
- An alert, by email or chat, when a job has failed a set number of times.
- A simple way to resend a single order after the problem is fixed.
- A daily check that compares the number of orders in both systems.
Cost
This does not require a large project. A queue, an idempotency check, signature verification and a log cover most of it. Built in from the start, they cost a day or two. Added after the first month of duplicate invoices, they cost that plus the clean-up.
Planning for retries, outages and duplicates from the start is what lets the business stop checking the numbers by hand.