Skip to content

Direct fiscal POS/ERP

The direct @timbro/fiscal lane is for a POS or ERP that already owns the sale and needs to fiscalize it. That operation creates a fiscal operation against Timbro; it does not create a Payment, hand off to Checkout, or expose payment-provider concepts. Keep sale ownership, internal numbering, and lines in the POS/ERP.

The approved flow has two separate lanes:

  1. For collection, the server uses @timbro/payments and payments.create; authorization and its Payment have their own states.
  2. To fiscalize a sale already paid by another method, the server uses @timbro/fiscal with its fiscal credential and the sale’s immutable revision. Do not mix a Checkout reference or tender write-back into that operation.

The direct adapter receives issuer identity, buyer, lines, and an idempotency key. Store sourceRecordId and sourceRevision; on timeout retrieve the same operation instead of creating another. Fiscal operation states are accepted, processing, issued, rejected, unavailable, and operator_required; they never translate into Payment states.

The fiscal client’s intended shape makes those recovery keys explicit:

import { createTimbroFiscal, type SourceDocument } from "@timbro/fiscal";
const baseUrl = process.env.TIMBRO_FISCAL_ORIGIN;
const credential = process.env.TIMBRO_FISCAL_CREDENTIAL;
if (!baseUrl || !credential) throw new Error("Missing fiscal origin or credential");
const fiscal = createTimbroFiscal({
baseUrl,
credential,
});
const sourceDocument: SourceDocument = {
kind: "consumer",
buyer: { identityKind: "anonymous" },
sale: {
currency: { kind: "dop" },
declaredTotals: { net: "1050.85", tax: "199.15", total: "1250.00", nonBillable: "0.00" },
lines: [{ sourceLineId: "sku-1", description: "Service", quantity: "1", measurementUnit: "unit", unitPrice: "1250.00", itemKind: "service", taxTreatment: "itbis_18" }],
paymentTerms: { terms: "cash" },
},
};
const idempotencyKey = "pos-sale-123-r4"; // derive this from the immutable sale revision
const operation = await fiscal.fiscalOperations.create({
source: { recordId: "pos-sale-123", revision: 4, contentSha256: "...sha256-of-canonical-sale..." },
idempotencyKey,
action: { kind: "issue", document: sourceDocument },
printedRepresentation: { profile: "receipt_80mm" },
});

The published fiscal package must preserve the semantics of sourceRecordId, sourceRevision, digest, and idempotency; do not add a paymentId or checkoutUrl to this lane.

Before writing a tender back, reconcile POS response, acquiring authority, and fiscal result separately. If a boundary is uncertain, leave the sale under review and assign operational recovery.