Waver for Craft CMS

Lite free · Pro $99 · Craft Commerce 5

Your store, in your books

Waver records each completed Craft Commerce order in Wave as one balanced entry. Included tax comes back out of income, discounts land on a contra account, and no order is posted twice.

Waver

One order, one entry

A Wave money transaction: the deposit into the account the money actually reached, split across the accounts it belongs to. You map the accounts once; Waver works out the lines for every order, and they add back up to the order total without a fudge line.

text
Order #A1B2C3                          DEPOSIT  178.45  →  Business Checking

  Items       Sales              credit  150.00   included tax already taken out
  Shipping    Shipping Income    credit    9.95
  Sales tax   Sales Tax          credit   30.50   a liability, not revenue
  Discounts   Discounts          debit    12.00   contra-income account

  150.00 + 9.95 + 30.50 − 12.00 = 178.45      drift 0.00

Features

What Waver does after an order completes, and what it refuses to do.

Tax inside the price, handled

When tax is included in the price, Commerce keeps it inside the item subtotal. Push that subtotal across as sales and the tax you owe shows up as income. Waver books sales as the subtotal minus included tax, and tax to your sales tax account.

  • A £200 line at 20% VAT books 166.67 sales and 33.33 tax
  • Stores that add tax at checkout get the same entry

Never the same money twice

Wave's API can't read a money transaction back, so Waver writes its record before calling Wave, keyed on an id derived from the order with a unique index behind it. A retried job, a second completion event or a backfill finds that record and stops.

  • A send that never got an answer stays pending
  • It is never re-sent automatically — a person checks Wave first

The preview is the payload

Every Commerce order screen gets a Preview entry button. It builds the entry with the same code the real sync, the console dry run and every retry use, so the rows you approve are exactly what Wave receives.

Refusals, not surprises

When an order can't be recorded correctly, Waver says why before it calls Wave: an account this order needs isn't mapped, only part of the total was captured, or the lines are further from the total than the rounding tolerance.

  • Sub-cent drift goes to a rounding account, up to 0.05 by default
  • Beyond that, the entry is refused and the difference saved

Automatic, off the request

When an order completes, Waver queues a job and checkout carries on, so a customer never waits on Wave and an outage at Wave can't block a sale. Pro can trigger on an order status instead, such as shipped.

Customers matched by email

Email is the only way Wave can look up a customer, so that's the key. Matches are cached, a missing customer can be created, and an order with no email is recorded without one rather than creating a blank customer every time.

Invoices and refunds (Pro)

Create a Wave invoice instead of a ledger entry, optionally approved and emailed by Wave. A successful Commerce refund becomes its own withdrawal; the original sale is never edited.

  • Invoice lines need a Wave product, so Waver maps one per SKU
  • Wave invoices can't hold shipping — such orders are refused, not shortened

Routing for bigger stores (Pro)

Map each Commerce gateway to its own payment account and each store to its own Wave business. Anything unmapped uses the default.

A log of every call

Wave reports business-rule failures as an HTTP 200 with didSucceed: false, so Waver reads the body rather than trusting the status code, and logs a refusal as an error with Wave's reason. Lite keeps seven days without payloads; Pro keeps the full request and reply for as long as you choose.

Waver in the control panel

The preview on an order, the records screen, a record in doubt and the connection log, from a Craft 5 install running Commerce.

The Preview entry dialog on a Commerce order, showing what Wave would be sent: a 174.04 anchor to Stripe Clearing, credits to Sales, Sales Tax and Shipping Income, a 27.00 debit to Discounts, and the note Balanced and ready to send
The Wave records screen listing orders with their status — synced with a Wave id, failed, skipped, and one pending, attempted and unconfirmed
A pending Waver record whose send timed out, with the explanation that it may or may not be in Wave and will not be sent again on its own, the Ask Wave, Mark as recorded and Force resend buttons, and the exact payload that was sent
The Wave connection log showing successful calls, a timed-out moneyTransactionCreate, and a refused one logged as an error with Wave's reason even though Wave answered with HTTP 200

Screenshots from a working install with a demo store, not mockups.

Rehearse before you send

The console dry run prints the entry the real sync would send, and backfill shows every older order it would record before it writes anything. Accounts appear as the Wave ids you mapped.

bash
$ php craft waver/sync/order 1042 --dry-run
Order A1B2C3 — Order A1B2C3
  anchor DEPOSIT 178.45 QWNjb3VudDozOTQ1
  sales        QWNjb3VudDo0MT dr            cr 150.00     Items
  tax          QWNjb3VudDo0Mj dr            cr 30.50      Sales tax
  shipping     QWNjb3VudDo0Mz dr            cr 9.95       Shipping
  discount     QWNjb3VudDo0ND dr 12.00      cr            Discounts

$ php craft waver/sync/backfill --dry-run --since=2026-01-01

Frequently Asked Questions

Common questions about recording Craft Commerce orders in Wave.

Start free, upgrade when you need to

Lite is free and records every completed order as a balanced Wave transaction. Pro is $99 with a $49/year renewal, and adds invoices, refunds, routing and the full log.

More in Accounting & ERP

Pairs well with Waver

Commerce orders, landed in the books your client already uses. One plugin per accounting package, built around the tax rules of the country it's sold in, plus Erpy for when the client runs a full ERP.