Skip to content
~/tariqul.islam
/projects

/projects/zatca-aorabooks

2024–2026

ZATCA e-invoicing for Aorabooks — compliance at the counter

client:
Aorabooks tenants selling in Saudi Arabia
role:
Technical Project Manager
  • Laravel
  • Vue.js
  • MySQL

01 — the problem

What was actually at stake

ZATCA Phase 2 made cryptographically signed, QR-coded e-invoices mandatory for Saudi businesses — with a fine schedule for getting it wrong. Aorabooks tenants needed compliance built in, not bolted on, and multi-tenancy made it harder: every tenant has its own certificates, its own invoice chain, its own clearance state.

And the counter is hostile territory — printers jam, networks drop mid-sale. Compliance could never be allowed to stop a till.

02 — the architecture

How it was built

ZATCA lives as its own bounded service: UBL 2.1 XML generation, per-tenant invoice hash chains, digital signing, TLV QR payload assembly, and submission to the clearance API. The design rule that mattered most: sign locally, print immediately with the QR, and run clearance through a retry ledger — a ZATCA outage queues re-clearance instead of blocking sales.

Receipts print over ESC/POS to thermal printers with cash drawers on the same circuit; every invoice stores its full compliance artifact trail (XML, hash, signature, response) so an audit eight months later is answered from the ledger.

architecture — at a glance
compliance
ZATCA Phase 2 — UBL, hash chain, signing, QR, clearance
tenancy
per-tenant certificates & invoice chains
resilience
sign locally → print now → clear via retry ledger
hardware
thermal printers (ESC/POS), cash drawers

03 — the visuals

What it looks like

Illustrated architecture overview of ZATCA e-invoicing for Aorabooks — compliance at the counter
fig. 01 — /projects/zatca-aorabooks/cover

screenshot pending (NDA-safe crop)

drop /public/images/projects/zatca-aorabooks/01.jpg · 1600×900 · see docs/images.md

fig. 02 — screenshot, primary view

screenshot pending (NDA-safe crop)

drop /public/images/projects/zatca-aorabooks/02.jpg · 1600×900 · see docs/images.md

fig. 03 — screenshot, detail view

04 — from the codebase

ZATCA invoice clearance with a retry ledger

ZATCA invoice clearance with a retry ledger
public function clear(Invoice $invoice): ClearanceResult
{
    $xml = $this->ubl->render($invoice);          // UBL 2.1 document
    $signed = $this->signer->sign($xml);           // XAdES B-B signature
    $qr = $this->qr->payload($invoice, $signed);   // TLV base64 for receipt

    try {
        $response = $this->zatca->clearance($signed);
        $invoice->compliance()->create([
            'status' => 'cleared',
            'uuid' => $response->uuid,
            'qr' => $qr,
        ]);
    } catch (ZatcaUnavailable $e) {
        // Never block the till: queue for re-clearance, print with QR now.
        RetryClearance::dispatch($invoice)->backoff([60, 300, 900]);
        $invoice->compliance()->create(['status' => 'queued', 'qr' => $qr]);
    }

    return new ClearanceResult($qr);
}

05 — the outcome

What the numbers say

of invoices signed & QR-coded at print time
100%
sales blocked by clearance outages
0
artifact trail per invoice, audit-ready
full

Checkout speed stayed flat while every receipt became a signed legal document.

/projects/zatca-aorabooks/next

Need something in this neighborhood?

If this case study sounds like your problem — same domain, same scale, same kind of mess — I've already made the expensive mistakes for you. Tell me where you're stuck.