# Receipt export and auditor verification

> Send a payment, export its receipt, and let a third party verify it against the chain.

End-to-end: send a private payment, export a [receipt](/operations/receipts-and-viewing-keys) as a JSON file, hand it to an auditor, auditor verifies independently against the chain.

## Step 1: Send the payment and capture the receipt data

```ts
const toHex = (value: bigint): `0x${string}` => `0x${value.toString(16)}`;
const amount = parseUnits("0.5", 6);            // 0.5 USDC (bigint)
const prepared = await session.prepareTransfer({
    inputCommitments: [noteToSpend.commitment],
    amount: toHex(amount),
    tokenId: USDC_SEPOLIA,
    recipientDiscoveryData: { evmAddress: recipient },
});

const recipientPending = prepared.executeOptions.recipientPendingNotes[0];
const result = await session.relayTransfer(prepared.relayOptions[0]);

const receipt = {
    type: "privacy-pools-v2-payment-receipt",
    version: "1",
    pool: { chainId: 11155111, poolAddress, tokenId: USDC_SEPOLIA },
    payment: {
        fromSenderWallet: address,
        toRecipientWallet: recipient,
        amount: toHex(amount),
        amountFormatted: "0.5 USDC",
        txHash: result.txReceipt.txHash,
        explorerUrl: `https://sepolia.etherscan.io/tx/${result.txReceipt.txHash}`,
    },
    note: {
        commitment:      recipientPending.commitment,
        value:           recipientPending.value,
        tokenId:         recipientPending.tokenId,
        label:           recipientPending.label,
        ownerAddress:    recipientPending.ownerAddress ?? recipient,
        noteSecret:      recipientPending.noteSecret,
        noteAddressHash: recipientPending.noteAddressHash,
    },
    verification: {
        commitmentFormula: [
            "noteAddressHash = Poseidon(note.ownerAddress, note.noteSecret)",
            "precommitment   = Poseidon(noteAddressHash, note.tokenId, note.value, 0x0)",
            "commitment      = Poseidon(precommitment, note.label)",
        ],
        onChainCheck: "PoolVault.commitments(commitment) returns non-zero",
    },
};

await persistReceipt(receipt);
```

## Step 2: Hand the JSON to the auditor

The receipt is a single self-contained JSON file, with no URLs, API keys, or external dependencies to manage. It does carry the note's `noteSecret`, which reveals the payment to anyone who reads it, so deliver it over a secure channel and never post it publicly.

**Frontend transport.** The JSON above is the inner payload, not necessarily the surface a user shares. The v2 frontend encodes the same receipt into a base64url string and hands it off through a route rather than as a loose file: an `/import-note/{encoded}` link for the recipient, and a `/verify`-style audit route for the auditor. The encoded link and the JSON file carry the same data, so an auditor can verify either one.

## Step 3: Auditor verifies, client-side

The auditor pastes / drops the JSON into a verifier page (see the Payroll PoC's `/audit` route for a working example). The verifier runs four checks:

```ts
// 1. Parse + shape validation
const r = ReceiptSchema.parse(jsonBlob);

// 2. Recompute noteAddressHash locally
const hash = await PoseidonHashService.create();
const computedNAH = hash.hash([r.note.ownerAddress, r.note.noteSecret]);
assert(BigInt(computedNAH) === BigInt(r.note.noteAddressHash));

// 3. Recompute commitment locally
const pre = hash.hash([r.note.noteAddressHash, r.note.tokenId, r.note.value, "0x0"]);
const commitment = hash.hash([pre, r.note.label]);
assert(BigInt(commitment) === BigInt(r.note.commitment));

// 4. Confirm the commitment is on-chain at the claimed pool
const client = createPublicClient({ chain: sepolia, transport: http(rpcUrl) });
const ts = await client.readContract({
    address: r.pool.poolAddress,
    abi: POOL_VAULT_ABI,
    functionName: "commitments",
    args: [BigInt(r.note.commitment)],
});
assert(ts !== 0n);
```

When all four checks pass, the payment is proven. The auditor learned nothing about any other transaction in the sender's wallet.

## What this gives an accountant

-   An auditable record per payment without invasive [viewing-key](/concepts/keys) disclosure.
-   Independence: they verify against the chain, not against the sender's claims.
-   Selectivity: one receipt covers exactly one payment. No other activity exposed.

For the cryptographic background, see [Selective disclosure](../concepts/selective-disclosure). For an implementation example, see the [Payroll PoC](https://github.com/0xbow-io/PoC-Payroll) source (`apps/payroll-server/src/routes/proof-submit.ts` for the export endpoint and `apps/payroll-client/app/audit/page.tsx` for the verifier UI).
