RAWA Engineering

Designing a POS for Jordan's JoFotara e-invoicing: a reference architecture

فريق هندسة RAWA · 2026-11-09

ملخص بالعربية: ورقة مرجعية بالإنجليزية عن معمارية ربط نقطة البيع بالفوترة الوطنية الأردنية (جو فوترة): حفظ وطباعة البيعة أولاً، محاولة فورية بحدّ 3 ثوانٍ، طابور إعادة محاولة بنفس الـUUID لمنع التكرار، وقاطع دائرة مشترك، وإشعارات دائنة تشير للفاتورة الأصلية.

Whitepaper · RAWA Engineering · rawaerp.com · open guide and runnable example: github.com/rawaerp/jordan-jofotara-pos-integration-guide

Problem

JoFotara requires every tax invoice a POS issues to be reported to the Income and Sales Tax Department. Treated as "POST an XML", the integration fails in exactly the place restaurants cannot tolerate: at the till, at peak. A restaurant rings a sale every 30–40 seconds at rush hour; a 15-second call to a government API visibly grows the queue. The engineering question is not how to build the XML — it is how to keep selling when the API is slow, down or strict.

Requirements

  1. The cashier never waits on JoFotara.
  2. No duplicate invoices, even across timeouts and retries.
  3. No lost invoices, even if the terminal or the service goes down mid-flight.
  4. Diagnosable rejections the operator can fix.
  5. Returns (credit notes) that reference the original invoice.
  6. Per-taxpayer isolation of credentials in a multi-tenant system.

Architecture

sale saved + receipt printed (never blocked)
        │
        ├─► instant attempt  (3 s budget, pooled keep-alive connection)
        │        ├─ cleared → store QR + UUID → print QR
        │        └─ timeout / error ─┐
        └────────────────────────────┴─► background queue (retries, same UUID)
                                              │
                       shared circuit breaker: 3 failures / 2 min → skip instant path for 60 s

Failure modes and how the design answers them

Failure Without the design With it
JoFotara slow (10 s+) Cashier stalls, queue grows Sale completes; invoice clears from the queue
Timeout after the server accepted the request Retry creates a duplicate Same UUID: idempotent
Service down 20 minutes Every cashier waits out every timeout Breaker opens; queue drains on recovery
Terminal offline Invoice never leaves the device Queued locally; submitted in order on reconnect
Validation rejection Bare "ERROR", support ticket Specific message shown; resubmit after fixing

Test checklist

Cash sale with tax · credit sale to a customer with a tax number · invoice-level discount · service charge · return as credit note · service unreachable (cashier not blocked, no duplicate after recovery) · rejection with readable reason · two terminals selling concurrently (counter strictly increasing). Runnable example and a longer checklist: github.com/rawaerp/jordan-jofotara-pos-integration-guide.

Result

RAWA ships this architecture with JoFotara included in every plan (200 / 400 / 600 JOD per year) and invoices sent from the cashier screen. Reference: https://rawaerp.com/guide/pos-with-jofotara-jordan

Common questions

How do you avoid duplicate JoFotara invoices when a request times out?
Generate the invoice UUID once on the client, store it with the sale and reuse the same UUID on every retry, so a retry is an idempotent re-submission instead of a second invoice.

Should the cashier wait for JoFotara?
No. Save and print the sale first, make one instant attempt with a short budget (3 seconds), and send anything that did not clear from a background queue.

What happens when JoFotara is down?
A shared circuit breaker stops the instant attempt for a cool-down window (for example 60 seconds after three failures in two minutes) while the queue keeps retrying, so cashiers keep selling.