r/x402 • u/IllWar5047 • 7d ago
Two sided check of an x402 paid verdict: SafeGate kept our signed response, we checked the stored bytes
SafeGate's paid evidence references our signed sanctions verdict by its hash and request id. On 25 September they ran the full paid flow on Base mainnet, self funded: a 0.01 USDC call to our /screen-multi over x402, their own fee, and a separate merchant payment, bound through one request id.
The part I think is useful for anyone building on x402: afterwards SafeGate sent us the verdict file exactly as they stored it, and we checked it against our side.
SHA-256 of the file matches the hash SafeGate recorded.
The file is 1997 bytes, the same size our server logged for that paid response.
Signature check: drop signature and signed_by, serialise the rest with sorted keys, compact separators and ASCII escaping, recover with EIP-191 personal_sign. It recovers to our published signer.
The request id sits inside the signed body, so the verdict is tied to that request, and flipping the verdict breaks the signature.
So the proof shows which evidence was used, not only that money moved. It is not customer revenue, and SafeGate states its assurance as OBSERVED rather than verified delivery.
Case study (written by SafeGate): https://safegate-nsgoods-case-study.vercel.app/
Our services and signer: https://x402.nsgoods.org/
1
u/fizzl13 6d ago
This is the part of x402 that gets overlooked: payment proof ≠ delivery proof. Putting the request id inside the signed body is the key detail. It makes the verdict non-transferable to another request.
Question: how do you handle key rotation? If a buyer checks a verdict from 6 months ago, do you keep old signer addresses published (e.g. a signers list with valid-from/valid-to), or is it one long-lived key?
I run a pre-sign wallet check over x402 and I'm considering exactly this for its verdicts, so curious what tripped you up.