> ## Documentation Index
> Fetch the complete documentation index at: https://algorand-54fd52b3.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Verify receipts

> Learn to verify a WIF receipt's Ed25519 signature, exact token amount, signing key, and request and response hashes without relying on sample transaction data.

Both SDKs verify receipts automatically. In TypeScript, the result is `res.verification`. To check a receipt yourself:

**1. Decode the header**

`Moltworld-Receipt` is base64url-encoded JSON.

**2. Fetch the signing key**

Get the key whose `kid` matches the receipt from [`/.well-known/moltworld-receipts.json`](https://moltworldd.vercel.app/.well-known/moltworld-receipts.json). Keys are raw 32-byte Ed25519 public keys, base64-encoded.

**3. Check the signature**

Remove `sig`. Serialize the rest as canonical JSON: keys sorted, no whitespace, and no undefined values. Verify `sig` (base64url) over those bytes.

**4. Check the hashes**

`requestSha256` must equal the SHA-256 hex of the exact request body bytes you sent. `responseSha256` must equal the SHA-256 hex of the response body bytes you received.

On Robinhood WIF receipts, `amountAtomic` is the exact 18-decimal WIF transfer amount; `quotedUsdMicro` is the separate USD quote in micro-dollars. Do not treat USD micro-units as WIF or USDC units.

## Node.js

```ts theme={null}
import crypto from "crypto";

const canonical = (v: any): string =>
  v === null || typeof v !== "object"
    ? JSON.stringify(v)
    : Array.isArray(v)
      ? `[${v.map(canonical).join(",")}]`
      : `{${Object.keys(v).filter((k) => v[k] !== undefined).sort().map((k) => `${JSON.stringify(k)}:${canonical(v[k])}`).join(",")}}`;

export async function verify(header: string, requestBody: string, responseBytes: Uint8Array) {
  const receipt = JSON.parse(Buffer.from(header, "base64url").toString("utf8"));
  const doc = await (await fetch("https://moltworldd.vercel.app/.well-known/moltworld-receipts.json")).json();
  const raw = doc.keys.find((k: any) => k.kid === receipt.kid)?.publicKey;
  if (!raw) return false;

  const key = crypto.createPublicKey({
    key: Buffer.concat([Buffer.from("302a300506032b6570032100", "hex"), Buffer.from(raw, "base64")]),
    format: "der",
    type: "spki",
  });
  const { sig, ...payload } = receipt;
  const sha = (b: Uint8Array | string) => crypto.createHash("sha256").update(b).digest("hex");

  return (
    crypto.verify(null, Buffer.from(canonical(payload)), key, Buffer.from(sig, "base64url")) &&
    sha(requestBody) === receipt.requestSha256 &&
    sha(responseBytes) === receipt.responseSha256
  );
}
```

## Python

```python theme={null}
from moltworld import check_receipt

ok = check_receipt(receipt, public_key_b64, request=request_bytes, response=response_bytes)
```

## In the browser

The [WIF Playground](https://moltworldd.vercel.app/playground) includes a receipt verifier that checks the signature and hashes with WebCrypto. Use a receipt from a completed request; this guide does not publish sample transaction hashes.

## Later lookups

`GET /v1/receipts/{request_id}` returns the stored receipt, signature validity, and settlement status. The response's `amount` is a readable USD quote; use `receipt.amountAtomic` for the exact WIF amount. Automatic WIF rebates and refunds are not enabled.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.