Draft v0.1 · open spec · no custody

x402 receipt verification

Verify an x402 payment receipt: amount, asset, network, payee, tx hash, mandate, usage and refund. Proof of Spend v0.1 spec and validator.
Written scope and written report — no calls. No wallet access for the first scan.

200 OK is not a payment

x402 makes payment part of the HTTP request. A 402 challenge is followed by an authorization and a settlement. The response can be 200 while the receipt is wrong, missing, replayed, or for a different payee.

  • Amount, asset, network, payee
  • Transaction hash and settlement time
  • Mandate and expiry
  • Usage × unit price
  • Refund and remaining amount
  • Replay / receipt ID
  • Verdict and policy version
  • On-chain verification (v0.2)

Proof of Spend v0.1

A signed JSON receipt. Draft schema, example and validator. Not a token, not custody.

Spec Validator

What the validator checks

schema → signature → mandate expiry → amount
→ usage × price → refund ≤ payment → verdict = allow
→ receipt id

v0.1 does not query the chain; it says onchain_verified=false explicitly. That is the honest boundary.

Run it yourself

No account and no key of ours: generate a keypair, sign the example receipt, verify it. Then move the amount by one cent and watch the signature break.

python3 -m nidhipati_proof keygen --out-dir keys
python3 -m nidhipati_proof sign examples/proof-of-spend.example.json --key keys/private.pem
nidhipati-proof verify examples/proof-of-spend.example.json.signed.json --pubkey <b64 from keygen>

# OK
#   ✓ schema   ✓ signature_alg: Ed25519   ✓ signature: Ed25519
#   ✓ mandate_expiry: settled <= expires  ✓ usage_amount: quantity * unit_price == amount
#   ✓ refund: 0 <= refund <= amount

# after changing the amount by one cent:
# error: signature: invalid
# error: usage: quantity * unit_price does not match payment amount
# exit code 1

Synthetic example; the verification is real, local, and needs no service of ours.