AffixIO SDK for Offline Payments and Merchant Integration

Add offline acceptance logic, proof generation, anti-replay controls, local queueing, and reconnect sync to your existing merchant and terminal stacks. The SDK is a control layer, not a PSP or acquirer, and works with your current payment provider.

Who the offline payment SDK is for

The AffixIO SDK is built for teams that need offline payment acceptance, proof-based verification, anti-replay protection, and terminal reconnect sync without replacing their existing payment stack.

Merchants

Retailers and businesses that want to accept payments when connectivity fails. The SDK adds local acceptance logic, proof generation, and queue-and-sync so your existing merchant or POS provider keeps handling authorisation and settlement.

PSPs (payment service providers)

Processors and gateways that want to offer offline-capable flows to their merchants. Integrate the SDK as a control layer: your rails stay in place; the SDK handles offline decisioning, proofs, and sync. Existing PSP integration patterns apply.

Terminal providers

Hardware and software vendors building or embedding payment terminals. The SDK runs on Node 18+, browser, or Deno; zero runtime dependencies and a small deploy footprint (~300KB dist-only) suit older or resource-constrained devices.

Smart POS vendors

Smart terminal and all-in-one POS builders who need offline payment integration, tokenised card handling, and proof-based audit. The SDK fits between your POS application and your chosen acquirer or processor.

Ticketing platforms

Event and ticketing systems that accept payments at gates or box offices with unreliable connectivity. Use the SDK for offline acceptance and proof-based verification; pair with ticket and access control as needed.

Access control systems

Physical access, membership, and gated venues that combine payment with entry. The SDK provides offline payment verification and reconciliation support without storing card data.

Eligibility systems

Government, benefits, or eligibility verification programmes that need payment or proof-of-payment as part of a flow. Use the SDK for privacy-first, proof-based payment acceptance and audit.

What the SDK does

In plain terms, the AffixIO SDK does the following. It does not move money, replace Visa/Mastercard, or act as an acquirer.

Full stack architecture

The AffixIO SDK sits between the terminal and your existing payment infrastructure. AffixIO does not settle money.

Online path (simplified):

Customer → Terminal / POS → AffixIO SDK (validation, proof, optional server check) → your merchant PSP / acquirer / processor → card rails or banking rails. Settlement and authorisation remain with your PSP or acquirer.

Offline path:

Customer → Terminal / POS → AffixIO SDK (local acceptance, proof, queue). No network call. When back online: SDK sends queued transaction packages to the AffixIO API; the API validates and stores proofs. Your upstream processor still handles authorisation and settlement when you send them the same transactions (or you reconcile batch output with your processor).

So: the SDK and API provide offline decisioning, proof-based verification, anti-replay, and queue-and-sync. They do not replace your PSP, acquirer, or card scheme connection. For more on how AffixIO fits into your stack, see technical architecture.

How the AffixIO SDK works

  1. Terminal gets tokenised payment input. The terminal or POS supplies a token (e.g. from EMV or tokenisation), amount, and optional currency, customer ID, payment method. No PAN is passed to the SDK.
  2. SDK derives stable card fingerprint. A one-way derivative of the token (e.g. SHA-256 of a prefixed token, truncated and base64url) is used as a stable card fingerprint for exposure and cross-terminal checks.
  3. SDK derives transaction nullifier. A unique value per attempt is derived from card fingerprint, merchant ID, site ID, terminal ID, amount, currency, counter, epoch, time bucket, and nonce. This is the replay key.
  4. SDK checks replay, card exposure, and merchant rules. Replay: is this transaction nullifier already recorded? Card exposure: is this card within allowed pending count and value and time window? Cross-terminal: does the pattern (e.g. same card on multiple terminals in a short window) violate policy? All checks are local.
  5. SDK accepts or declines locally. If any check fails, the SDK returns decline (or “require online” where configured). Otherwise it proceeds.
  6. SDK creates proof envelope. A compact proof (e.g. HMAC-SHA256 commitment plus nullifier, under 90 bytes) is generated and bound to the token and amount.
  7. SDK queues pending offline transaction. When offline, the transaction and proof are appended to a local queue. When online, the SDK may submit immediately to the API.
  8. SDK syncs transaction package when connection returns. Queued items are sent to api.affix-io.com (or your configured base URL) in batches. Each item includes transaction ID, proof data, amount, currency, and optionally transaction nullifier and card fingerprint.
  9. Upstream processor handles money side. You or your integration sends authorised transactions to your PSP/acquirer for authorisation and settlement. AffixIO does not move funds.
  10. AffixIO reconciles state. The API stores proofs, rejects duplicate transaction nullifiers, and returns sync results so the SDK can update local state (e.g. clear exposure for confirmed transactions).

Offline payments flow

Online path

Terminal has connectivity. Payment input is validated; replay and card-exposure checks run locally; proof is generated. If terminal is registered, the API can perform an additional validation step. The transaction is then sent to the API immediately; on success, the SDK marks it synced and can update local exposure. The merchant or PSP still sends the transaction to the acquirer/processor for authorisation and settlement.

Offline path

Terminal has no connectivity. Payment input is validated; replay, card exposure, and cross-terminal checks run locally; proof is generated. The transaction is written to the local queue and the SDK returns accepted with queued: true. No PAN is stored. The terminal can continue accepting payments within policy until the queue is flushed.

Reconnect path

When the connection is detected again, the SDK’s connectivity monitor triggers a sync. Queued transactions are sent in batches (e.g. 50) to the API. The API validates each payload, rejects replays by transaction nullifier, and stores proofs. Successes are removed from the queue; failures remain for retry. Card exposure state can be reduced for confirmed transactions so the card can be used again within policy.

Double-spend prevention

The SDK treats “double-spend” as: the same payment attempt replayed, or the card over its allowed unsynced offline exposure, or a usage pattern that breaks the merchant’s offline policy. It does not mean “same card seen once ever”; repeat purchases are allowed within policy.

Exact transaction replay prevention

Every payment attempt has a unique transaction nullifier derived from card fingerprint, merchant, site, terminal, amount, currency, terminal counter, offline epoch, time bucket, and nonce. If that nullifier is already recorded (locally or on the API), the attempt is rejected as a replay. No exceptions.

Per-card unsynced exposure limits

For each card (by stable fingerprint), the SDK tracks: number of pending offline transactions, total pending value, first and last pending time, and terminals used. Configurable policy (e.g. max 3 pending, £50 cap, 30-minute window) determines allow, “require online”, or reject. When transactions are synced and confirmed, exposure is reduced so the card can be used again within limits.

Cross-terminal anomaly detection

If the same card is used on too many terminals in a short window, or with impossible velocity (e.g. two terminals in under 20 seconds), the SDK can reject. This limits distributed abuse while allowing legitimate multi-terminal use when policy allows.

Merchant-defined policy windows

You set limits such as max pending count, max pending value, offline time window, max terminals per card, and minimum time between terminals. The SDK enforces these locally; the API does not hold card-level state for offline decisions.

Transaction lifecycle

States are effectively: pending (queued, not yet synced), confirmed (synced and recorded by the API), failed (e.g. sync error, server rejection), expired (e.g. not synced within a retention window, if you implement expiry). Replay and exposure checks use the same lifecycle so double-spend prevention stays consistent.

Implementation guide

  1. Request API key. Get access from AffixIO. You will use it for terminal auth and sync. Demo keys (e.g. from api.affix-io.com) are for testing only.
  2. Install the SDK. npm install @affix-io/affixiomerchant. Zero runtime dependencies; you can deploy only the dist/ folder (~300KB) to terminals.
  3. Configure merchant and terminal identity. Pass apiKey, and optionally terminalId, terminalSecret, merchantId, siteId. Terminal registration (via API) enables server-side validation when online.
  4. Connect tokenised terminal output. Your POS or terminal provides a token (never PAN), amount, and optionally currency, customer ID, payment method. Pass these into pay().
  5. Define offline policy. Set offlinePolicy (e.g. max pending count, max pending value, offline window, terminal spread, velocity). Defaults are provided; tune for your risk and use case.
  6. Handle proof generation. Proofs are created inside the SDK. Optionally enable a proof database (file or custom store) for audit; the API also stores proofs on sync.
  7. Queue transactions. When offline, the SDK queues automatically. Ensure sufficient storage (e.g. storageDir) and handle queued: true in your UI (e.g. “Payment accepted; will sync when online”).
  8. Implement reconnect sync. Sync runs in the background when connectivity returns. You can call syncNow() manually (e.g. end of day) and use queueSize() and isOnline for status.
  9. Reconcile outcomes. Use sync result (uploaded, failed, errors) and your proof store or API proof records to reconcile with your acquirer or processor batch output.
  10. Expose status in merchant UI. Show online/offline, queue depth, and last sync result so staff know when payments will sync and when to retry or go online.

SDK features table

FeatureWhat it doesWhy it matters
Offline acceptanceDecides accept/decline locally using replay, exposure, and policyPayments work without connectivity; no blocking on network.
Proof generationBuilds a compact proof (<90 bytes) bound to token and amountVerification and audit without storing card data.
Transaction nullifierUnique per-attempt value; same value = replayPrevents duplicate submission of the same payment.
Card exposure controlLimits pending count and value per card, with time windowControls offline risk; you set limits.
Cross-terminal checksFlags same card on many terminals or impossible velocityReduces distributed double-spend and abuse.
Local queueStores accepted offline transactions until syncNo data loss when offline; sync when back online.
Reconnect syncBatches queued transactions to the API when onlineAutomatic reconciliation and proof storage.
Proof databaseOptional local and API storage of proofsAudit trail and reconciliation support.
Zero runtime depsNo node_modules required on terminal; dist-only deployLightweight footprint for older or constrained devices.
Pluggable storageSwap file storage for Redis, SQLite, or customFits your infrastructure and scaling needs.

Sample SDK and API usage

SDK init

import { AffixioMerchant } from '@affix-io/affixiomerchant';

const merchant = new AffixioMerchant({
  apiKey: 'affix_your_key_here',
  terminalId: 'TERM-001',      // optional
  terminalSecret: 'term_...',  // optional
  baseUrl: 'https://api.affix-io.com',
  storageDir: '.affixio',
  merchantId: 'MERCHANT-01',
  siteId: 'STORE-01',
});
await merchant.setup();

You pass your API key and optional terminal and site identifiers. setup() loads persisted state and starts the connectivity monitor. No PAN or raw card data is ever passed to the constructor.

Create payment attempt

const result = await merchant.pay({
  cardToken: 'tok_chip_abc123',  // from your POS hardware
  amount: 24.99,
  currency: 'GBP',
  paymentMethod: 'chip',
});

cardToken is the tokenised reference from your terminal (e.g. after EMV or tokenisation). The SDK validates input, runs replay and exposure checks, generates a proof, and either syncs immediately (if online) or queues. Result includes accepted, transactionId, synced, queued, and optional error or requireOnline.

Derive proof (low-level)

Proof generation is normally internal to pay(). If you need to work with proofs directly, you can use generateProof, serialiseProof, and deriveCardFingerprint / deriveTransactionNullifier from the SDK. Proof format: HMAC-SHA256 commitment plus nullifier, serialised to under 90 bytes.

Queue and sync

const pending = await merchant.queueSize();
const syncResult = await merchant.syncNow();
// { uploaded: 3, failed: 0, errors: [] }

queueSize() returns how many transactions are waiting to sync. syncNow() flushes the queue to the API in batches. Failed items stay in the queue and retry on the next sync.

Inspect status

const isOnline = merchant.isOnline;

Use isOnline to show connectivity status in your UI. When the terminal reconnects, sync runs automatically in the background.

API endpoints used by the SDK

The SDK talks to api.affix-io.com (or your baseUrl): GET /health (connectivity), POST /api/merchant/terminals/authenticate (terminal token), POST /api/merchant/payments/validate (optional server-side check), POST /api/merchant/transactions/sync (upload queue). All use Authorization: Bearer <token>. Sync payload includes transactionNullifier and cardFingerprint when available for replay check and proof storage.

Security and privacy

Use cases

FAQ

How does the AffixIO SDK work offline?

The terminal receives a tokenised payment (e.g. from the reader). The SDK runs replay and card-exposure checks locally, generates a proof, and either sends the transaction to the API (if online) or appends it to a local queue. When connectivity returns, the queue is synced in batches. No PAN is stored; authorisation and settlement remain with your PSP or acquirer.

Does AffixIO replace my PSP or acquirer?

No. AffixIO provides an SDK and API for offline decisioning, proof generation, anti-replay, queueing, and reconnect sync. You keep your existing relationship with your payment service provider or acquirer for authorisation and settlement. The SDK is a control layer that works with your current stack.

Do I need a Visa or Mastercard API?

No. AffixIO does not require direct integration with Visa or Mastercard. Your terminal or POS already connects to card rails through your acquirer or processor. The SDK adds offline logic and proofs on top of that flow.

How does AffixIO prevent double spending?

Three mechanisms: (1) Replay. each attempt has a unique transaction nullifier; if it’s seen again, the attempt is rejected. (2) Card exposure: per-card limits on pending count and value (and time window) so a card cannot exceed your policy offline. (3) Cross-terminal: same card on too many terminals or impossible velocity can be rejected. All checks run locally; the API also rejects duplicate nullifiers on sync.

What happens when the terminal reconnects?

The SDK’s connectivity monitor detects the connection and triggers a sync. Queued transactions are sent to the API in batches. The API validates payloads, rejects replays, and stores proofs. Successful items are removed from the queue; failed ones stay for retry. Local card exposure state can be updated so the card can be used again within policy.

Does AffixIO store card data?

No. The SDK and API work only with tokenised references (e.g. cardToken). Card fingerprints and transaction nullifiers are one-way derivatives. No PAN or full card data is stored or transmitted by AffixIO.

Can I use the SDK on smart POS terminals?

Yes. The SDK runs on Node 18+, browser, and Deno. It has zero runtime dependencies and can be deployed as a small dist bundle (~300KB). Smart POS and all-in-one terminals that can run Node or a browser can integrate the SDK.

Can I use the SDK for tickets and access systems too?

Yes. The same SDK can support offline payment acceptance at gates, box offices, or kiosks. For ticket-specific logic (e.g. scalping prevention, seat assignment), combine with AffixIO’s ticketing and access APIs as needed.

Get started

Request API access to integrate the AffixIO SDK with your terminals, merchant applications, or platform.

Get API access

To discuss your terminal, merchant, or platform integration in detail, contact us. For technical details, see the technical architecture and the npm package.