x402 Payment Integration Security Audit — Methodology v1

2026-09-03 · minia2a audit · Audits the resource provider (an agent/platform/gateway selling API services over x402), not the buyer.

🛡️ Run the $5 buyer-side trust check: submit any x402 endpoint to /x402/payment-audit and see whether it resists forged payments, replay, and honeypot payTobefore you pay. Full published standard (v2): github.com/minia2auk/x402-payment-security-checklist.

x402 = HTTP 402 Payment Required. A buyer agent receives a 402 challenge (price/payTo) → pays (USDC) → the resource verifies → delivers. Insecure integrations let a caller steal results without paying (forged payment), consume one payment many times (replay), bypass via race, drain an over-authorized allowance, or lose/lock funds.

We built this checklist from running an x402 gateway in production ourselves — every live lesson below is a bug we hit and fixed on minia2a. If you run an x402 resource, this is the checklist we would apply to you.

1. Payment verification authenticity

Risk: the resource trusts request headers (PAYMENT-SIGNATURE / X-PAYMENT / txHash fields) without actually verifying on-chain settlement → an attacker forges headers and gets results free.

Checks

Live lesson (minia2a): our gateway has two verify paths — Path A (facilitator verify/settle) and Path B (on-chain receipt, traversing transfer logs to confirm USDC reached payTo). Failure returns 402/502, never delivers.
Fix: complete on-chain verification before delivery; verify failure always refuses; one centralized verify function, no bypass.

2. Replay protection (nonce / txHash reuse)

Risk: the same payment (txHash/signature) is reused → pay once, consume N times.

Checks

Live lesson (minia2a): replay_lock DB table + gateway in-memory cache, key = path+txHash. Pitfall: a test txHash once needed a gateway restart to clear the in-memory cache (DB delete alone wasn't enough) — in-memory replay protection dies on restart, so the DB is the durable line.
Fix: persist replay keys in DB with a unique constraint; scope the key with the service id to stop cross-service reuse; mark a consumed txHash on refund.

3. Delivery timing & race (web↔chain sync)

Risk: delivery-before-verification (concurrent/async path returns results before verification completes → free results); web↔chain desync (web says "paid" but chain hasn't settled, or chain settled but web state is tamperable); same-order concurrent consumption double-spending service resources.

Checks

Live lesson (minia2a): our "Free Shopping fix" — settle first, then deliver (payment confirm → ledger → then call the service). A liveness probe runs before delivery: if the service is down we refuse to settle (neither collect for nothing nor deliver for free).
Fix: deliver atomically only after verification + ledgering succeed; order state via atomic ops (DB transaction / unique constraint); web-layer state is untrusted — chain is the source of truth.

4. Allowance / permit2 authorization

Risk: an integrator authorizes max uint256 allowance → the payee (or a compromised payee) can drain the full allowance; permit2 approvals with no expiry/scope leave long-term exposure; the payee can change payTo after authorization, redirecting funds.

Checks

Live lesson: x402 uses permit2-exact (authorize + single spend). Our challenge carries assetTransferMethod: permit2-exact — single-spend authorization, deliberately not max approval.
Fix: permit2 single-spend (exact), not max; clear/expire after use; payTo changes need governance + notice.

5. Collection & fund safety

Risk: misconfigured payTo (money sent to the wrong address, or self-send rejected); insecure/single-point platform-wallet key; collected funds can't move (no gas / no op access); refund logic exploitable (over/duplicate refunds).

Checks

Live lesson (minia2a): we hit self_send_not_allowed — payTo was configured as the same address as the paying wallet and the facilitator rejected all self-sends, taking the platform's payments down. payTo must be an independent collection address ≠ the payer wallet. Our refund path settles on the chain where payment actually occurred (not a handler default), so refunds never land on the wrong chain.
Fix: independent payTo + permissioned changes; layered wallet keys; gas reserve; refunds on the actual payment chain + permission check + duplicate protection.

Severity grading

SeverityFlaw
CriticalForged payment delivers (1), replay consumes unlimited (2), delivery-before-verification enables free use (3)
HighOver-authorization drainable (4), payTo hijackable (5), refund abusable (5)
MediumIn-memory-only replay (lost on restart), weak web↔chain state consistency

How we audit

  1. White-box: read the resource's gateway/service code, walk it against this checklist.
  2. Black-box: send crafted requests at x402 endpoints — forged payment headers, reused txHash, concurrent same-order, tampered payTo — and observe whether results are stolen, duplicated, or hijacked.
  3. On-chain: verify the challenge's payTo, the actual receiving address, and authorization amounts.

Framework v1 — built from operating an x402 gateway plus the x402 spec. The published v2 standard (six categories + liveness pre-filter) lives in the reference repo.