An order is paid at eleven at night, the confirmation email goes out, and nothing has reached the Zakat, Tax and Customs Authority. Your store recorded a sale; the e-invoice is a separate document that has to be built, signed with your establishment's certificate, given a compliant QR code and transmitted. It is owed on every paid order, and the gap between a live store and invoiced orders is where too many merchants sit.
What your store platform does, and what it does not
Shopify and WooCommerce hold everything an invoice needs: items, quantities, discounts, shipping, the money actually taken. Neither reports to ZATCA, and neither holds your establishment's certificate, your invoice counter or the hash chain linking each document to the one before it.
A working setup has one shape: the store announces a paid order, and an e-invoicing system turns it into a compliant document, signs it and sends it to the Authority. Ours does that through the Shopify app, the WooCommerce plugin or the REST API. Shopify invoices an order the moment it is marked paid; WooCommerce when it is marked paid, processing or completed, whichever comes first. Cash on delivery invoices when you mark it paid.
Almost every order is a simplified invoice
A simplified tax invoice is the consumer document: handed over immediately, reported to the Authority within 24 hours. A standard tax invoice carries the buyer's VAT number and address and must be cleared before it reaches them (the full comparison is here). A store's order table is mostly the first kind, and the amount decides as much as the buyer: under article 53 of the VAT Implementing Regulations a simplified invoice is permitted below SAR 1,000 whoever the buyer is, while a supply of SAR 1,000 or more to a VAT-registered buyer must carry a full tax invoice. Marking an order as a business purchase cannot promote a 460-riyal sale.
Neither integration makes a human choose per order. WooCommerce puts an optional "buying as a business" field in the checkout: a valid VAT number with a complete national address produces a cleared tax invoice, and below the threshold it stays simplified with the number printed on it. Shopify prints a buyer-typed number too, but clears a tax invoice only for a buyer you confirmed as an establishment in the panel, or a native Shopify B2B purchasing company.
Neither withholds an invoice: when the buyer's details cannot produce a valid tax invoice, the simplified one is issued anyway with a notice naming what was missing. A buyer VAT number that is present must be fifteen digits starting and ending with 3, or BR-KSA-44 rejects the document; missing address elements only warn, under BR-KSA-63.
What gets issued, per event
| In the store | What is issued | Why |
|---|---|---|
| A paid consumer order | Simplified tax invoice | Reported within 24 hours |
| SAR 1,000+, valid buyer VAT number, full address | Standard tax invoice, cleared | Mandatory at that amount |
| The same buyer, below SAR 1,000 | Simplified, VAT number printed | Article 53 permits it |
| A full refund | Credit note for the invoice value | An invoice is never edited |
| A partial refund | Credit note for the returned lines | The rest of the supply stands |
| A return that moves no money | Nothing | The sale did not change |
| An order cancelled before invoicing | Nothing | No document to correct |
| A test order, or a non-SAR order | Nothing — skipped, with the reason | Not a completed SAR sale |
Installing the app is not the last step
This is the failure that costs merchants most. Connecting your store and connecting your establishment are two different things. The store connection lets us see orders. The establishment connection is your onboarding with ZATCA: a certificate request, one OTP from the Fatoora portal, compliance checks, a production certificate in your name. Until that certificate exists there is nothing to sign an invoice with.
What happens in between is the trap. The store stays open, orders keep arriving, and each one fails for that single reason and is recorded as a failed order rather than an invoice — nothing is queued for later. A merchant who never came back to finish onboarding can go months believing invoicing is handled while not one document has been issued. We email the owner when orders are waiting, but check your invoices page after your first real sale.
Finishing the connection fixes everything from that point forward, and only from that point: past failures are not back-filled. On WooCommerce the order still lives in your own database, so the ZATCA Tools screen in WordPress offers a Retry per order. On Shopify the order arrived as a webhook that is gone once processed, so those are issued by hand. Onboarding itself is one OTP from the Fatoora portal, used once, from the right environment — the walkthrough has the details.
The invoice has to total what the customer paid
Saudi shelf prices include VAT, so store prices do too, and the invoice has to work backwards from a number that already contains the tax. That is where a halala goes missing, and a customer who paid 125.00 does not accept an invoice for 125.01.
Take two mugs at 45.00, a bag of coffee at 10.00 and 25.00 of delivery: 125.00 paid, tax included. Each line converts to its price before VAT, rounded half-up to the halala — the mugs 39.13 each (78.26 for the line), the coffee 8.70, delivery 21.74, giving 108.70. Multiply that rounded figure by 15% and you get 16.305, which rounds to 16.31 and an invoice for 125.01. Worked out once from the value before rounding it is 16.30, and 108.70 plus 16.30 is exactly 125.00 — ZATCA's XML Implementation Standard asks for final results to be rounded, not intermediate ones.
One constraint cannot be argued away: a line's net must equal quantity times unit price exactly, so some baskets have no two-decimal price that lands on the right total. The halalas that cannot be fitted are declared as a document-level allowance rather than added to what the customer owes. It is settled on our side, identically for a store order, a typed invoice and an API call.
A refund is a credit note, not an edit
An invoice that has been cleared or reported cannot be changed or deleted, so a refund becomes a second document referencing the first and carrying a reason — a note without one is rejected under BR-KSA-17. The note inherits the form of its invoice, so a note against a cleared tax invoice is itself cleared (credit and debit notes covers the rest).
The value follows the money actually returned. Continue the order above: the customer returns the coffee and 10.00 goes back to the card. The note carries 8.70 plus 1.30 of VAT, totalling 10.00 — where 15% of the rounded 8.70 would have reversed 1.31, more VAT than the invoice ever charged. A note never credits more than the invoice has left open. A refund for an order we never invoiced produces nothing: there is no original to reference, so the order is marked with that reason.
WooCommerce adds cases, because the plugin watches the order indefinitely: cancelling an invoiced order produces a full credit note, restoring it produces a debit note, and editing it issues the difference. Nothing is deleted; both documents stay as the audit trail.
Riyals only
We issue in SAR and nothing else. An order in another currency is recorded as skipped with that reason rather than converted, because the rate used would be printed on a tax document in your name. Sell to Saudi Arabia in riyals and those orders invoice automatically; the rest stay yours to handle.
What this does not do
It is not an accounting system: no ledger, no journals, no inventory, no payroll. It does not manage stock or fulfilment. There is no multi-currency and no recurring invoicing. If your platform already issues compliant signed invoices and reports them itself, you do not need us.
What it does is the narrow part: every paid order becomes a signed invoice with its QR code, cleared or reported as its type requires, the signed XML archived six years and downloadable, credit notes raised from refunds, rejections shown with the Authority's own code and a link to its guide, per-branch devices, and sales reports. Already running an ERP? Connecting it through the API is the smaller change, and the error reference explains what comes back. Free to start: 50 invoices or 30 days from the day you connect, whichever comes first, then from 49 SAR a month — start here. Then finish the connection before your next sale.