API reference
Every export from @obscura-app/merchant-sdk, one section per symbol.
obscura(config)
The merchant-side factory. Returns a ObscuraMerchantClient — a small
object whose only method is charge().
ts
import { obscura } from "@obscura-app/merchant-sdk";
const pay = obscura(config: MerchantSdkConfig);MerchantSdkConfig
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
merchantEtaAddress | string | Yes | — | Solana pubkey (base58) of your Umbra encrypted token account. NOT a regular SPL wallet. |
network | "solana" | "solana-devnet" | No | "solana-devnet" | Which Solana network to verify on. |
mint | string | No | devnet / mainnet USDC | SPL token mint to accept. Must match the Obscura backend's STABLECOIN_MINT. NOTE: devnet today runs on WSOL — pass mint='So11111111111111111111111111111111111111112' explicitly when network='solana-devnet'. |
decimals | number | No | 6 | Decimals for the mint. USDC = 6 (mainnet default). WSOL = 9 (devnet default — pass decimals: 9 explicitly). |
rpcUrl | string | No | Solana public RPC | RPC endpoint for getTransaction reads during verification. Helius recommended for production. |
replayWindowMs | number | No | 300000 | Window during which a queueSignature is rejected on duplicate (default 5 min). |
The factory throws synchronously on a missing or malformed
merchantEtaAddress.
pay.charge(config)
Returns an Express-compatible middleware. Runs before your route
handler, intercepts payment-less requests with a 402, and verifies
payment-bearing requests on-chain before calling next().
ChargeConfig
| Option | Type | Required | Description |
|---|---|---|---|
amount | string (atomic units, e.g. "10000") | Yes | Price per call. |
description | string | No | Human-readable description included in the 402 body. |
mimeType | string | No | Response MIME advertised. Default application/json. |
maxTimeoutSeconds | number | No | Max client wait for payment, in the 402 body. Default 300. |
asset | { address: string; decimals: number } | No | Per-route override of the SDK-default mint + decimals. |
Side effects on res
On a successful verification, the middleware:
- Sets an
X-Payment-Responseheader — base64-encoded JSON of the settlement object. - Sets
res.locals.obscuraSettlement— the parsed settlement object. - Calls
next()so your downstream handler runs.
See Settlement receipts for reading them.
Re-exports
PaymentRequirements— the x402 type for a 402 challenge body. Useful for typing custom 402 handlers.
ts
import type { PaymentRequirements } from "@obscura-app/merchant-sdk";
type PaymentRequirements = {
scheme: "exact";
network: "solana" | "solana-devnet";
asset: string;
amount: string;
payTo: string;
resource: string;
maxTimeoutSeconds: number;
description?: string;
mimeType: string;
};ObscuraMerchantClient— the type returned byobscura().
Protocol-level notes
- Verification is direct chain read. The middleware calls
getTransaction(queueSignature)(andgetTransaction(callbackSignature)if present) via your configured RPC. No facilitator, no Obscura backend dependency at runtime. - Settlement is implicit. By the time the agent presents the envelope, the queue tx has landed and Arcium MPC has finalized. Nothing for the merchant to settle — the on-chain mixer commitment IS the settlement. Your encrypted balance is credited asynchronously by Obscura's claim daemon.
- Replay protection is in-process. The SDK keeps an in-memory
Map<queueSignature, seenAt>and rejects duplicates withinreplayWindowMs. For multi-process deployments, back this with Redis to close the cross-process replay window. - No Obscura API calls from the middleware. The SDK talks only to Solana RPC. Zero dependency on Obscura infrastructure on the request path.