Skip to content
Veldway
← WorkReference Design

WooCommerce order flow into accounting and CRM without retyping

Scenario: B2B wholesaler selling through WooCommerce

A worked design for wiring WooCommerce into accounting and CRM systems — the after-the-sale plumbing that most store builds leave as somebody's manual job.

The problem

B2B wholesalers commonly run WooCommerce for ordering, an accounting platform for invoicing, and a CRM for the sales team — connected by a person who retypes. Each retype is a chance for a wrong price band, a missed credit limit, or an invoice that never gets raised. Sales calls customers without knowing what they ordered last week; accounting chases payments the store already recorded. The systems are individually fine; the business between them runs on manual labor.

Requirements

  • Every WooCommerce order becomes an accounting invoice with correct tax treatment and customer matching — automatically
  • Customer records deduplicated and matched across store, accounting, and CRM despite inconsistent naming
  • B2B realities handled: credit terms, purchase-order references, partial payments
  • Sales team sees order history and payment status in the CRM without asking anyone
  • A human-readable log of every record the automation created or updated

The solution

A WordPress plugin plus a small sync service. The plugin captures order lifecycle events cleanly (including status transitions Woo's own hooks make awkward) and hands them to the service, which owns the cross-system logic: customer identity resolution, invoice creation with tax mapping, CRM timeline updates, and payment-status flow back to the store. The plugin stays thin on purpose — business logic in a WordPress plugin is where testability goes to die.

Architecture

WooCommercethin plugin · signed eventsSync serviceowns all cross-system logicIdentity resolvermatch → review queueAccountinginvoices · payments · taxCRMcompanies · timelinespayment webhooksAudit tableevery write logged

The WordPress plugin registers order-lifecycle hooks and posts signed events to the sync service. The service resolves customer identity first (match by tax ID, then email domain, then fuzzy name with a human-review queue for low-confidence matches), then creates or updates the accounting invoice and CRM records. Payment webhooks from the accounting side flow back to update WooCommerce order status. Every external write is recorded in an audit table the admin UI reads.

Challenges & resolutions

Customer identity across systems is the hard problem: 'ACME Ltd', 'Acme Limited', and 'acme.com purchasing' are one customer to a human and three to an API.

Resolution — A layered matcher: exact tax-ID match auto-links, email-domain match auto-links with a log entry, and anything fuzzier lands in a review queue where a human confirms once — after which the mapping is remembered forever. Automation handles the routine; humans handle the genuinely ambiguous, exactly once.

WooCommerce order statuses don't map one-to-one onto accounting document states — a 'processing' order might be paid, part-paid, or on credit terms.

Resolution — Instead of mirroring statuses, the design defines explicit state mappings per payment scenario (prepaid, credit-terms, partial), with the accounting platform's payment records as the source of truth for money and Woo as the source of truth for fulfillment.

Tax treatment differs by customer country and category, and a wrong default produces legally incorrect invoices silently.

Resolution — Tax mapping is a declared table, not inference: unknown combinations block invoice creation and alert a human rather than guessing. The failure mode is a queued invoice, never a wrong one.

Why this design exists

Store builds usually end at the thank-you page. The expensive part of running a B2B store starts there: invoicing, payment chasing, and keeping sales informed. This worked design covers that flow end to end — spelled out rather than asserted — using realistic B2B mess: inconsistent customer names, credit terms, partial payments.

Design notes

The plugin is deliberately dumb. It observes order lifecycle events, signs them, and posts them to the service. All cross-system logic lives outside WordPress where it can be unit-tested, deployed independently, and survive a site theme rebuild untouched. WordPress plugins that embed business logic become the thing nobody dares update.

Identity resolution before anything else. No invoice or CRM write happens until the customer is resolved to a single identity across systems. Getting this wrong quietly is how businesses end up with six copies of their biggest customer and receivables assigned to the wrong three.

Block, don't guess. Two places in the flow refuse to proceed without a human: low-confidence identity matches and unknown tax combinations. Both produce a queued item and an alert. An automation that guesses on money or legal documents hasn't removed manual work — it's converted it into forensic work later.

What we'd adapt per client

The adapters: the design speaks Xero-shaped and HubSpot-shaped APIs, and a real engagement swaps in QuickBooks, Zoho, Salesforce, or Pipedrive equivalents. The identity matcher's rules get tuned to the actual customer data — the review-queue pattern stays, because every real dataset has ambiguity that deserves a human decision.

What the design guarantees

  • Order-to-invoice needs no human touch for matched customers; unmatched ones need exactly one confirmation, ever
  • Sales sees live order and payment history in the CRM timeline instead of asking accounting
  • Every automated record links back to its trigger — auditable end to end
  • The pattern generalizes: the accounting and CRM adapters are designed as swappable modules, specified against Xero-style and HubSpot-style APIs

Have a similar problem?

Describe the problem in plain language — broken, slow, manual, or missing. We'll tell you honestly whether and how we can help.

  1. 01We reply within one business day
  2. 02A short call to understand the problem
  3. 03A written scope and fixed quote — no obligation