WooCommerce via bizuno-api #
Selling on WooCommerce and keeping the books in Bizuno is one of the most common
Bizuno topologies. The bizuno-api WordPress plugin is the bridge. The thing
to get right up front is who owns what: each resource flows in one
direction, and there is no two-way merge.
Architecture #
bizuno-api is a WP plugin installed on the WooCommerce site. Bizuno itself
may live:
- on the same site (the
bizuno-accountingplugin is active) — the bridge
short-circuits to an in-process call, or - on a separate back-office host — the bridge talks to it over HTTPS, using a
configured Bizuno URL and API token (Settings → Bizuno → General).
Communication is two complementary directions, each with its own transport:
- Plugin → Bizuno: HTTPS calls to Bizuno’s portal API (
portal/api.php) —
e.g.orderAdd,shipGetRates— authenticated with the Bizuno API token. - Bizuno → Plugin: Bizuno calls REST endpoints the plugin registers under
bizuno-api/v1/—product/update,product/refresh,product/sync,
order/confirm— authenticated with WordPress credentials.
Who owns what (data flow) #
Each resource is unidirectional. Memorize this table and most “why didn’t it
sync?” questions answer themselves:
| Resource | Direction | How |
|---|---|---|
| Orders | Woo → Bizuno | Plugin posts the order to orderAdd |
| Customers | Woo → Bizuno | Created/matched in Bizuno when their order imports |
| Inventory (stock) | Bizuno → Woo | Bizuno pushes to product/refresh |
| Prices (+ tiers) | Bizuno → Woo | Bizuno pushes to product/refresh / product/update |
| Products (create/update) | Bizuno → Woo | Bizuno pushes to product/update |
| Shipping rates | Woo → Bizuno (live) | At checkout, Woo asks Bizuno via shipGetRates |
| Ship confirmation | Bizuno → Woo | Bizuno posts tracking/status to order/confirm |
WooCommerce owns orders and customers; Bizuno owns products, stock, and
pricing. The catalog is published down to the store; sales come up to the
books.
Authentication #
Two separate credentials, one per direction:
- Plugin → Bizuno: a shared API token, sent as the
X-Bizuno-Token
request header and checked by Bizuno’svalidateApiToken()with a constant-time
compare. (Bizuno then binds the call to a configured API user so permissions
apply.) - Bizuno → Plugin: the plugin’s REST endpoints authenticate with a WordPress
email+pass(verified viawp_authenticate, requiring the
manage_woocommercecapability).
The
decrypt_passwordquirk — why tokens are handled oddly. Older installs
stored the token/password as plaintext; a naive base64-decode of a plaintext
value produced binary garbage, which a reverse-proxy WAF (mod_security)
then rejected as a malformed header — a 400 before PHP ever saw the request.
The fix:decrypt_password()only decodes values that carry the encrypted-form
::separator and otherwise returns the value verbatim. So a plaintext
token stays printable and passes the WAF. If you’re debugging auth failures,
this is why the token isn’t blindly decoded.
Failure modes (mostly WAF, in practice) #
The bridge’s hard-won lessons are all about getting clean requests past a
hardened reverse proxy:
Authorization: Basicwas dropped entirely — some mod_security rule sets
flag Basic-auth patterns and 400 at the proxy. The token rides in
X-Bizuno-Tokeninstead.- Empty query parameters are stripped before the request is built, to avoid
WAF fuzzing/enumeration false positives. - Non-printable header bytes are avoided (the
decrypt_passwordquirk above).
When a call fails, the plugin logs the HTTP error to the WordPress message stack
and merges any message block from Bizuno’s JSON response — there’s no
automatic retry, so a failed sync is re-triggered, not self-healed. An empty
response is surfaced as a likely TLS/connection issue.
Sync triggers — no webhooks #
Be precise here, because the topology invites the wrong assumption: bizuno-api
does not use WooCommerce webhooks, and does not poll. Sync happens by:
- Order export: automatically on the WooCommerce thank-you hook after
checkout (if “Autodownload Orders” is enabled), or manually via an Export to
Bizuno button on the order. - Catalog push (stock/price/products): manual actions in Bizuno — a bulk
upload, or a “Quick Refresh” that batches items throughproduct/refreshin an
AJAX loop. Not scheduled. - Shipping rates: live at checkout (Woo calls
shipGetRatesin real time). - Ship confirmation: manual from Bizuno (pick a date, confirm → posts to
order/confirm). - The only cron is a WordPress cron for image/thumbnail processing — not for
order or inventory sync.
So “near-real-time” means “on checkout and on demand,” not an event stream.
Conflict resolution: there isn’t any #
There is no conflict detection, locking, or versioning — and because each
resource is one-directional, you rarely need it if you respect ownership:
- Edit a customer or order in WooCommerce; the next export overwrites Bizuno’s
copy. A Bizuno-side edit to those is discarded on the next import — last write
from Woo wins. - Edit stock or price in Bizuno; the next push overwrites WooCommerce’s copy.
A store-admin edit there is overwritten on the next refresh — last write from
Bizuno wins.
The rule that keeps you out of trouble: edit each resource only on its owning
side. Editing a Bizuno-owned field in WooCommerce (or vice versa) isn’t merged —
it’s eventually overwritten.
Related #
- REST API surface — the
portal/apiendpoints this plugin calls, and the token model - EDI X12 — the other major external-system integration
