Skip to main content
A single store serves two purposes in the facturas SDK: it records immutable voucher reservations so retries never produce duplicate invoices, and it caches WSAA login tickets so multiple workers share the same authenticated session. The SDK ships adapters for Postgres, Redis, file storage, and in-memory — or you can implement the ArcaStore interface directly against any backing store you already use.
Use a Postgres or Redis store in any production or staging environment. The in-memory store is convenient for tests and local examples but does not survive a restart and cannot be shared across processes.

How stores are used

Voucher idempotency

Before authorizing a voucher, the SDK writes an immutable reservation keyed by your idempotencyKey. On a retry with the same key, the SDK looks up the reservation, confirms the input matches, and queries ARCA for the outcome — without issuing a second CAE request.

WSAA session cache

A configured store caches WSAA login tickets so that a warm process reuses a valid ticket obtained by another worker. Providing an explicit wsaaSessionStore targets ticket caching only and takes priority over the unified store.

Postgres

createPostgresStore wraps any client that exposes a parameterized query function. Neon, Supabase Postgres, Vercel Postgres, pg, and postgres all work — results can be a row array or a { rows } object. Provision the table once before first use:
Then pass your client’s query function to the adapter:
You can also pass a custom table option if you need to use a different table name. The value must be a simple SQL identifier ([a-zA-Z_][a-zA-Z0-9_]*):
Atomic voucher reservation uses INSERT ... ON CONFLICT DO NOTHING RETURNING key. The adapter never creates the table or acquires a database-level lock — provisioning is your responsibility.

Redis

createRedisStore accepts an ioredis or Upstash client. The SDK auto-detects the flavor by checking for a call method (ioredis) or falling back to Upstash. Pass { flavor: "upstash" } to override detection explicitly.
Do not enable key eviction (maxmemory-policy) on the Redis instance that backs your store. Evicted voucher reservation keys can cause a retry to issue a duplicate invoice. Use a dedicated Redis database or instance without eviction for the store, separate from any cache instance you use for other purposes.
The adapter uses SET key value NX (ioredis) or set(key, value, { nx: true }) (Upstash) for atomic reservation writes. No TTL is applied.

File store

createFileStore persists records to a local directory. Keys are hashed to filenames; writes use an exclusive-create + atomic-rename strategy to avoid partial writes. Files are created with mode 0600 and new directories with 0700.
Use the file store on single-server deployments with a durable, private volume. It is not suitable for multi-worker or serverless deployments where workers run on different hosts and cannot share a filesystem. The file store does not provide cross-process locking.

Memory store

createMemoryStore keeps records in a plain JavaScript Map for the lifetime of the process. It serializes WSAA ticket refreshes within the shared object, but records do not survive a restart.
Use the memory store in tests and local examples. Do not use it in production or in serverless functions where processes are short-lived.

Custom store

Implement the ArcaStore interface to connect any backing store:
The add method must be atomically exclusive — it must return false without modifying the stored value if the key already exists. This is the operation that makes voucher idempotency safe under concurrent retries.

Store key structure and record lifetime

Store keys follow these patterns:
  • WSAA tickets: arca:v1:wsaa:{environment}:{service}:{fingerprint}
  • Voucher reservations: arca:v1:attempt:{environment}:{taxId}:{idempotencyKey}
Reservation records contain the input hash, operation, reserved voucher coordinates, and the exact input sent to ARCA. They contain fiscal and customer data — restrict access to the store and protect backups accordingly.
Never delete, expire, or overwrite voucher reservation records. The SDK creates reservations and never overwrites them with results. Deleting a reservation can cause a subsequent retry to issue a duplicate invoice. Records must be treated as append-only for the lifetime of your application.
Reservation records carry a format version. v: 1 records are plain WSFE reservations readable by any SDK version from 0.9 onward. v: 2 records are WSMTXCA or itemized reservations — they always identify their provider, and SDK version 0.10 cannot replay them, which prevents a rollback from re-sending a WSMTXCA voucher through WSFE by accident. Preserve both versions. Repeating an idempotency key with a different input, operation, provider, or explicit voucher number is an idempotency mismatch and throws ARCA_INPUT_IDEMPOTENCY_MISMATCH.

Using one store for both purposes

You can pass the same store instance to store (for voucher idempotency) and rely on it automatically for WSAA ticket caching. The SDK namespaces all keys, so a single Postgres table or Redis database handles both safely:
If you need independent expiry policies or access controls, pass a separate wsaaSessionStore to override ticket caching while keeping the unified store for voucher idempotency.