@affix-io/affixiomerchant
v1.1.2
Published
Offline and agentic payment SDK for AffixIO merchants — accept card payments without internet, enable AI agents to transact autonomously, prevent double-spend, auto-sync. Zero runtime dependencies.
Maintainers
Keywords
Readme
@affix-io/affixiomerchant
Offline and agentic payment SDK for AffixIO merchants.
Accept card payments without internet, enable AI agents to transact autonomously, prevent double-spend with local nullifiers, and auto-sync when connectivity returns. Zero runtime dependencies — runs on any Node 18+ terminal, browser, or Deno.
Patent Pending — AffixIO's ZKP-based proof system for offline and agentic payments is patent pending. All rights reserved.
Features
- Agentic payments — AI agents can call
pay()directly; idempotency, double-spend, and proof are all handled - Offline-first — generates proofs locally, queues transactions, syncs on reconnect
- Zero runtime dependencies — pure TypeScript, ships < 25 KB compiled JS
- < 90 byte proofs — compact HMAC-SHA256 commitment bound to card token + amount
- Double-spend protection — nullifier checked locally in < 1 ms before any network call
- All payment methods — chip, contactless, NFC, QR, magstripe, Apple Pay, Google Pay, Samsung Pay
- Aggressive reconnect — exponential backoff from 1 ms → 5 s, instant revival on reconnect
- Any runtime — Node 18+, browser, Deno (uses Web Crypto API, no native modules)
- Pluggable storage — swap in Redis, SQLite, or any custom backend
Install
'''bash curl -X POST https://api.affix-io.com/api/demo-key '''
Get a free API key from AffixIO - 3 Requests per minute.
npm install @affix-io/affixiomerchantNo peer dependencies required.
Quick start
import { AffixioMerchant } from '@affix-io/affixiomerchant';
const merchant = new AffixioMerchant({
apiKey: 'affix_your_key_here',
});
await merchant.setup();
const result = await merchant.pay({
cardToken: 'tok_chip_abc123',
amount: 24.99,
currency: 'GBP',
paymentMethod: 'chip',
});
console.log(result.accepted); // true
console.log(result.proofBytes); // always <= 90
console.log(result.synced); // true if online, false if queued offline
await merchant.destroy();Configuration
const merchant = new AffixioMerchant({
apiKey: 'affix_your_key_here', // required
terminalId: 'TERM-001', // optional — enables server-side validation
terminalSecret: 'term_secret_here', // optional — required when terminalId is set
baseUrl: 'https://api.affix-io.com', // optional — default shown
storageDir: '.affixio', // optional — persistent state directory
memoryOnly: false, // optional — in-memory only (no disk writes)
});| Option | Type | Default | Description |
|---|---|---|---|
| apiKey | string | required | API key from the AffixIO dashboard. |
| terminalId | string | — | Terminal ID registered with AffixIO. Enables server-side double-spend check. |
| terminalSecret | string | — | Terminal secret. Required when terminalId is set. |
| baseUrl | string | https://api.affix-io.com | Override the API base URL. |
| storageDir | string | .affixio | Directory for persistent nullifiers and queue. Created automatically. |
| memoryOnly | boolean | false | Use in-memory storage only. Useful for testing. |
Payment input
const result = await merchant.pay({
cardToken: 'tok_chip_abc123', // required — pre-tokenised from POS hardware
amount: 24.99, // required — major currency units (e.g. £24.99)
currency: 'GBP', // optional — ISO 4217, default GBP
customerId: 'cus_12345', // optional — for reconciliation
paymentMethod: 'chip', // optional — see payment methods below
transactionId: 'txn_my_id', // optional — auto-generated if omitted
metadata: { orderId: '789' }, // optional — passed through to the API
});Supported payment methods:
chip · contactless · nfc · qr · magstripe · manual · applepay · googlepay · samsungpay
Payment result:
{
accepted: boolean, // false if double-spend or server rejection
transactionId: string, // reference this in your systems
proofBytes: number, // always <= 90
synced: boolean, // true → submitted to API immediately
queued: boolean, // true → saved offline, will sync on reconnect
error?: string, // only present when accepted = false
}Agentic payments
Patent Pending — AffixIO's agentic payment architecture is patent pending.
AI agents can call pay() directly. The SDK handles idempotency, double-spend prevention, and cryptographic proof — no special agent integration required.
import { AffixioMerchant } from '@affix-io/affixiomerchant';
// Initialise once at agent startup — memoryOnly keeps state ephemeral
const merchant = new AffixioMerchant({
apiKey: process.env.AFFIX_API_KEY!,
memoryOnly: true, // no disk writes; safe for serverless / short-lived agents
});
await merchant.setup();
// Agent calls pay() autonomously — no human in the loop
async function agentCheckout(cardToken: string, amount: number) {
const result = await merchant.pay({
cardToken,
amount,
currency: 'GBP',
transactionId: `agent-${Date.now()}`, // deterministic ID prevents double-charge on retry
metadata: { agentId: 'checkout-bot-v2', sessionId: crypto.randomUUID() },
});
if (!result.accepted) {
throw new Error(`Payment rejected: ${result.error}`);
}
return result.transactionId; // pass to fulfilment pipeline
}Why it works for agents:
transactionIdsupplied by the agent → idempotent; retrying with the same ID cannot double-charge- Nullifier check is local and synchronous — no round-trip blocking the agent
memoryOnly: true— no filesystem access; safe inside Lambda, Deno Deploy, or Dockerqueued: trueresult means the charge is recorded even if the network drops mid-task- Proof is cryptographically bound to
cardToken + amount— unforgeable, non-replayable
Offline operation
pay() works with zero internet. Flow:
- Proof generated locally using Web Crypto (no network).
- Nullifier checked against local store — double-spend blocked in < 1 ms.
- If online with a terminal token → API validates server-side.
- Online → transaction submitted immediately →
{ synced: true }. - Offline → transaction saved to local queue →
{ queued: true }. - When connectivity returns → queue flushed silently in the background.
The terminal can accept payments indefinitely offline. Proofs are cryptographically bound to card token and amount — unforgeable and non-replayable.
Sync
// Automatic — triggered in the background when connectivity returns.
// Manual flush:
const result = await merchant.syncNow();
// { uploaded: 3, failed: 0, errors: [] }
// Queue depth:
const pending = await merchant.queueSize();Transactions upload in batches of 50. Failed ones stay in the queue and retry on next sync.
Connectivity monitor
online → ping every ~500ms
offline → 1ms → 1ms → 5ms → 50ms → 250ms → 1s → 2s → 5s (holds)
reconnect → reset to fast interval + auto-syncmerchant.isOnline // booleanPluggable storage
Default: nullifiers and queue persisted to <storageDir>/affixio-state.json.
Implement these interfaces to use Redis, SQLite, or any custom store:
import type { NullifierStorage, QueueStorage } from '@affix-io/affixiomerchant';
interface NullifierStorage {
has(nullifier: string): boolean | Promise<boolean>;
add(nullifier: string): void | Promise<void>;
delete(nullifier: string): void | Promise<void>;
size(): number | Promise<number>;
}
interface QueueStorage {
enqueue(tx: QueuedTransaction): void | Promise<void>;
dequeueAll(): QueuedTransaction[] | Promise<QueuedTransaction[]>;
remove(transactionId: string): void | Promise<void>;
size(): number | Promise<number>;
}Proof format
nullifier = SHA-256("nullifier:" + cardToken)[:18] → base64url (24 chars)
mac = HMAC-SHA256(key=cardToken, msg=amount|nonce|merchantId|ts)[:18] → base64url (24 chars)
proof_str = mac + "." + nullifier → 49 chars total (under 90 bytes)No raw card data is ever stored, transmitted, or logged.
Full API reference
import {
AffixioMerchant, // main class
ApiClient, // HTTP client (low-level)
DoubleSpendGuard, // nullifier guard
SyncEngine, // queue + upload engine
PingMonitor, // connectivity monitor
FileStorage, // file-backed storage
InMemoryNullifierStorage, // in-memory nullifier store
InMemoryQueueStorage, // in-memory queue
generateProof, // proof generation
serialiseProof, // proof → string
proofByteLength, // proof string byte length
parseConfig, // config validation
parsePayment, // payment input validation
} from '@affix-io/affixiomerchant';
import type {
MerchantConfig,
PaymentInput,
PaymentResult,
SyncResult,
CompactProof,
QueuedTransaction,
NullifierStorage,
QueueStorage,
CardToken,
} from '@affix-io/affixiomerchant';Endpoints used
| Method | Path | Purpose |
|---|---|---|
| GET | /health | Connectivity ping |
| POST | /api/merchant/terminals/authenticate | Terminal token |
| POST | /api/merchant/payments/validate | Server-side double-spend check |
| POST | /api/merchant/transactions/sync | Upload offline queue (batches of 50) |
All requests use Authorization: Bearer <token>.
Requirements
- Node.js 18+ — uses
fetch,crypto.subtle,node:fs/promises - Browser — any modern browser with Web Crypto API
- Deno — fully supported
- Zero runtime dependencies
License
UNLICENSED — © AffixIO. All rights reserved.
