obscura
docs nav
03Merchants

/docs/merchants/receipts

Settlement receipts

On every successful payment, the middleware exposes the verified envelope in two places:

  • res.locals.obscuraSettlement — the parsed settlement object, available to your route handler synchronously.
  • X-Payment-Response header — a base64-encoded JSON copy of the same object, sent on the wire so clients can verify on-chain.

Read it in your handler

Pull the queue signature into your response body, log to analytics, or write to your own DB.

server.ts·ts
import type { Response } from "express";

app.get(
"/article/:id",
pay.charge({ amount: "10000", description: "News article" }),
(req, res: Response) => {
  const settlement = (res.locals as {
    obscuraSettlement?: {
      scheme: "umbra-mixer-v1";
      queueSignature: string;
      callbackSignature?: string;
      recipient: string;
      amount: string;
      asset: string;
    };
  }).obscuraSettlement;

  res.json({
    id: req.params.id,
    headline: `Article #${req.params.id}`,
    body: "Full article text here…",
    queueSig: settlement?.queueSignature ?? null,
  });
},
);

Shape of the settlement object

ts
type ObscuraSettlement = {
scheme: "umbra-mixer-v1";
queueSignature: string;     // base58 Solana tx signature (the mixer queue tx)
callbackSignature?: string; // base58 Solana tx signature (Arcium MPC callback);
                            // omitted when the SDK reported pending finalization
recipient: string;          // base58 ETA pubkey (your merchantEtaAddress)
amount: string;             // atomic units string (e.g. "10000" = $0.01 USDC)
asset: string;              // base58 SPL mint address
};

Verification failures never reach your handler — the middleware intercepts them with a 402 + {error: "invalid_payment", reason} body and next() is not called. Your route only runs after a confirmed on-chain queue transaction.

Decode X-Payment-Response

Clients can verify settlement independently:

ts
const header = response.headers.get("X-Payment-Response");
if (header) {
const receipt = JSON.parse(
  Buffer.from(header, "base64").toString("utf8"),
);
console.log(
  `Paid! https://solscan.io/tx/${receipt.queueSignature}?cluster=devnet`,
);
}

Where it shows up on the dashboard

Every settlement lands in your merchant dashboard within a second via real-time SSE. Each row shows the route, the amount, the timestamp, and a Solscan deep-link to the queue transaction. The full feed is available at /merchants/payments with cursor pagination.

The encrypted-balance credit lands a tick later — once Obscura's claim daemon (every 2 minutes) scans the mixer tree and pulls the receiver-claimable UTXO into your account.