Fiscal

Authority-grade sales recording for apps that sell things. Koolbase Fiscal gives every transaction a gapless fiscal number, seals it as an immutable document, delivers it to the tax authority, and keeps the certification the authority grants — the signature and QR code a legal receipt must carry.

What problem this solves

A growing number of countries require every sales invoice to be reported to (or certified by) the tax authority in real time. Getting that right is harder than calling an API: numbers must be gapless and never reused, documents must be immutable once sealed, a network timeout must never produce a duplicate record, and the evidence must survive an audit years later.

Fiscal is the machinery for exactly that. Your app submits a sale; the engine does the rest.

Where it works today

Ghana (gh-gra) — live against GRA's Certified Invoicing System (E-VAT). Invoices, refunds, partial refunds, refund cancellations and purchase records are certified by GRA, and receipts carry GRA's signature and verification QR.

Everywhere else (kb-ref) — the reference adapter runs the full machine without an authority connection: gapless numbering, sealed immutable documents, complete evidence and retention. Useful anywhere e-invoicing isn't mandated yet, and the migration path when it becomes so is a device configuration change, not a rewrite.

The shape of it

Four concepts, and you only interact with the last two directly:

  • Merchant — the taxpayer. Their legal name and tax identity.
  • Device — a register: one till, one branch, one point of sale. Each device owns its own counter chain, so two tills never collide.
  • Intent — one transaction travelling through fiscalization. You create it with submit() and follow it with status().
  • Certification — what the authority granted: the signature, receipt number and QR your printed receipt must show.

The intent lifecycle

Every submission lands in one of these states. The distinction that matters most is before versus after a fiscal number is allocated — because an allocated number can never be reused or skipped.

StatusMeaningWhat you do
blockedA precondition failed before anything was consumed — no fiscal number was spent. blockedReason says why (e.g. the device has no usable credential).Fix the cause, resubmit the same clientRef. It resumes.
queued / submitting / retryingSealed, numbered, and travelling to the authority. isPending is true.Poll status().
fiscalizedThe authority answered and accepted. For certifying document kinds, the certification is present.Render the receipt.
attentionThe authority rejected the document, or its credential died after sealing. The register's chain waits behind it — nothing after it is submitted until it's resolved.An operator resolves it (see your fiscal dashboard / alerts).
voidedTerminally closed by an operator without fiscalization. Its number is spent and recorded as void — never reused.Nothing. It's final.

Retrying is always safe

You give every submission a clientRef — your own durable reference for the transaction, usually your local sale ID. Submitting the same ref twice always returns the same intent. It never creates a second one, never consumes a second fiscal number.

That single property makes every failure recoverable: if a submit times out, the sale may or may not have fiscalized — and you don't have to know. Submit it again, or poll it. Either way you converge on the truth.

What happens under a network failure

Inside the engine, a submission whose outcome is unknown is retried with byte-identical content until the authority answers definitively. Where the authority supports it (Ghana does), a duplicate submission is recognised and the original certification is fetched and adopted rather than re-issued. One sale, one number, one certification — regardless of how many times the network fails.

A minimal flow

// 1. Submit the sale. Your own sale id is the clientRef.
final result = await Koolbase.fiscal.submit(
  deviceId: registerId,
  clientRef: sale.id,
  payload: salePayload,   // shaped by the device's jurisdiction
);

// 2. Already certified? Print. Otherwise poll.
var intent = result;
while (intent.isPending) {
  await Future<void>.delayed(const Duration(seconds: 2));
  intent = await Koolbase.fiscal.status(
    deviceId: registerId,
    clientRef: sale.id,
  );
}

// 3. Render the receipt with what the authority granted.
if (intent.isFiscalized) {
  final cert = intent.certification;          // may be null for some kinds
  final qrUrl = cert?['qr_code'] as String?;  // Ghana: render as a QR image
}

Fiscal and offline

Koolbase's database writes queue transparently when a device is offline. Fiscal submissions deliberately do not. A register needs either a real answer or an explicit “not yet” — silently queueing a fiscalization would leave a cashier believing a receipt is legal when the authority has never seen it.

The pattern for offline-capable point of sale: record the commercial sale locally (the sale is a fact the moment money changes hands), and submit it fiscally when connectivity returns. Because clientRef is idempotent, that replay is a single call with no bookkeeping.