Server-side receipt and webhook verification helpers for the x402 payment protocol — Node.js & Python
Documentation · JWKS Endpoint · x402 Studio
When a buyer completes an x402 payment, the worker issues a signed receipt JWT containing the transaction details. When a seller webhook fires, the worker also signs the raw webhook body with an HMAC secret. This SDK helps you verify both layers safely.
Both the Node and Python implementations handle:
- receipt JWT verification via RS256 + JWKS
- webhook HMAC verification
- optional webhook-to-receipt cross-checking
- optional source-slug binding
npm install x402sglconst {
verifyX402ReceiptToken,
createX402ReceiptMiddleware,
verifyX402WebhookSignature,
verifyX402WebhookEvent,
} = require('x402sgl');pip install git+https://github.com/ivaavimusic/Singularity-SDK.git#subdirectory=pythonfrom x402layer_middleware import X402ReceiptVerifier, require_x402_receiptPython dependencies (installed automatically): PyJWT, cryptography. FastAPI is optional — install with pip install x402layer-sdk[fastapi].
const {
verifyX402ReceiptToken,
createX402ReceiptMiddleware,
verifyX402WebhookSignature,
verifyX402WebhookEvent,
} = require('x402sgl');verifyX402ReceiptToken(token, options?)
Verifies the receipt JWT and returns the decoded claims. Throws on invalid signature, expired token, or claim mismatch.
const claims = await verifyX402ReceiptToken(token, {
requiredSourceSlug: 'my-endpoint', // optional — prevents token replay
});createX402ReceiptMiddleware(options?)
Express middleware that reads the token from X-X402-Receipt-Token or Authorization: Bearer, verifies it, and attaches the claims to req.x402Receipt.
app.get(
'/v1/resource',
createX402ReceiptMiddleware({ requiredSourceSlug: 'my-endpoint' }),
(req, res) => {
res.json({ data: '...', payer: req.x402Receipt.payer_wallet });
}
);Returns 401 if the token is missing or invalid, 403 if the source slug does not match.
verifyX402WebhookSignature(rawBody, signatureHeader, secret, options?)
Verifies X-X402-Signature using the shared webhook secret.
verifyX402WebhookSignature(rawBody, req.headers['x-x402-signature'], process.env.X402_WEBHOOK_SECRET)verifyX402WebhookEvent(rawBody, signatureHeader, secret, options?)
Verifies the webhook signature, parses the JSON payload, and if data.receipt_token exists, verifies the receipt too.
const { payload, receipt } = await verifyX402WebhookEvent(
rawBody,
req.headers['x-x402-signature'],
process.env.X402_WEBHOOK_SECRET,
{
requiredSourceSlug: 'claude',
requireReceipt: true,
}
)
const purchaseId = payload.data.client_reference_idIf requireReceipt: true, the helper rejects webhook payloads that do not include data.receipt_token.
from x402layer_middleware import (
X402ReceiptVerifier,
require_x402_receipt,
verify_x402_webhook_signature,
verify_x402_webhook_event,
)X402ReceiptVerifier
verifier = X402ReceiptVerifier(
jwks_url="https://api.x402layer.cc/.well-known/jwks.json", # default
issuer="https://api.x402layer.cc", # default
audience="x402layer:receipt", # default
)require_x402_receipt(verifier, required_source_slug?)
FastAPI dependency that reads the token from X-X402-Receipt-Token or Authorization: Bearer, verifies it, and returns the decoded claims.
@app.get("/v1/resource")
async def resource(receipt=require_x402_receipt(verifier, required_source_slug="my-endpoint")):
return {"payer": receipt["payer_wallet"], "amount": receipt["amount"]}Raises HTTP 401 if the token is missing or invalid, HTTP 403 if the source slug does not match.
result = verify_x402_webhook_event(
raw_body,
signature_header,
secret,
verifier=X402ReceiptVerifier(),
require_receipt=True,
)
payload = result["payload"]
receipt = result["receipt"]
purchase_id = payload["data"].get("client_reference_id")verify_x402_webhook_signature(...) is also available if you only want HMAC verification.
| Claim | Type | Description |
|---|---|---|
event |
string |
Always "payment.succeeded" |
source |
string |
"endpoint" or "product" |
source_id |
string |
UUID of the paid resource |
source_slug |
string |
Slug of the resource |
amount |
string |
Payment amount (e.g. "1.00") |
currency |
string |
Asset symbol (e.g. "USDC") |
tx_hash |
string |
On-chain transaction hash |
payer_wallet |
string |
Buyer wallet address |
network |
string |
"base" or "solana" |
client_reference_id |
string | null |
Seller-supplied correlation id, if provided |
metadata |
object |
Seller-supplied correlation metadata, if provided |
status |
string |
Settlement status |
iat |
number |
Issued-at (Unix timestamp) |
exp |
number |
Expiry (Unix timestamp) |
jti |
string |
Unique receipt ID — use for idempotency |
| Property | Value |
|---|---|
| Format | JWT (JWS) |
| Algorithm | RS256 |
| JWKS URL | https://api.x402layer.cc/.well-known/jwks.json |
| Issuer | https://api.x402layer.cc |
| Audience | x402layer:receipt |
The token is delivered in the X-X402-Receipt-Token response header after a successful payment.
Seller webhook payloads are signed with:
X-X402-Signature: t=<timestamp>,v1=<hex_hmac_sha256>
The signed message is:
<timestamp>.<raw_request_body>
Current payment.succeeded payloads may include:
data.client_reference_iddata.metadatadata.receipt_tokendata.jwks_url
That makes deterministic purchase correlation possible for hosted checkout flows like:
/pay/request/claude?amount=1&client_reference_id=abc-123
- Always set
requiredSourceSlugin production to prevent receipt replay across resources. - Always verify the webhook signature before parsing or trusting the payload.
- If you rely on settlement authenticity, require and verify
data.receipt_token. - The SDK caches JWKS keys in memory (default 5-minute TTL) to avoid excessive network calls.
- To rotate signing keys: publish a new JWKS entry with a new
kid, then update the worker private key. Tokens signed with old keys remain verifiable until they expire.
MIT