On this page
x402 is a payment protocol built on HTTP. A client requests a resource and receives 402 Payment Required with a PAYMENT-REQUIRED header that lists the accepted payment options. The client signs one of them and retries with a PAYMENT-SIGNATURE header. The resource server, usually with a facilitator's help, verifies and settles the payment before it serves the content. See what x402 is and the three V2 headers for the full picture.
When an x402 implementation is wrong, nothing crashes. The resource unlocks for a signature nobody checked. A recipient address changes between two requests. A facilitator says yes to a forged authorization. The HTTP traffic looks normal, the logs look normal, and the money goes the wrong way or never arrives.
This article walks through eight failure modes we test for, what each one looks like on the wire, and the Settlens check that catches it. Every transcript below is real output from the Settlens CLI against its bundled demo target, a local server with a deliberately broken route for each failure shown. None of these tests needs a wallet or a private key, and none of them calls a facilitator's /settle endpoint.
1. Payment bypass: the resource fails open
The worst failure is also the simplest. The server checks that a PAYMENT-SIGNATURE header is present and serves the content without verifying it. Anyone who sends arbitrary bytes in that header gets the paid resource for free. It is a fail-open bug and a direct revenue leak, and it often survives code review because the happy path works perfectly: real clients send real signatures, so the missing verification never shows up in normal traffic.
The only reliable way to find it is to send an invalid payment and watch what happens. With the endpoint owner's explicit approval, the Settlens active probe builds one intentionally invalid authorization from the first advertised payment option (for an EVM option, an all-zero 65-byte signature from the zero address) and retries the request with it. If the resource answers with any 2xx status, security.invalid_signature_fail_closed fails at critical severity. A separate check, transport.invalid_payment_status, expects the status the HTTP transport defines: 400 for malformed payment data or 402 when verification or settlement fails.
settlens scan http://127.0.0.1:4402/bypass \
--active --authorized --authorization-ref DEMO-APPROVAL-002
Settlens x402 assessment eafd7f6a
Targets 1 · Active 1 · Settlement calls 0
127.0.0.1 GET http://127.0.0.1:4402/bypass
13 pass · 1 fail · 1 warn · 0 skip
FAIL critical Invalid payment authorization is denied
The endpoint returned HTTP 200 after an invalid payment authorization.
WARN medium Invalid payment uses a standard failure status
Expected HTTP 400 or 402 for invalid payment, received HTTP 200.
The scan exits with code 1, so a CI job stops here. Note the scope line: one active target, zero settlement calls. The probe proves the endpoint refuses a bad payment; it cannot create a good one.
Authorized testing only
The active probe sends traffic the endpoint owner must approve. Settlens refuses to run it without both --active and --authorized plus an --authorization-ref that names the approval, and it records that reference in the report.
2. The facilitator accepts a forged signature
Many resource servers delegate verification to a facilitator: they forward the payment payload to POST /verify and serve the content when the answer says isValid: true. If the facilitator's own verification is broken, every resource server that relies on it inherits a payment bypass, and none of them can see it from their own code.
When you pass --facilitator to an authorized active scan, Settlens sends the same invalid payload to the facilitator's /verify. It expects a conformant VerifyResponse, a JSON object with a boolean isValid (facilitator.verify_response_schema), and it expects that value to be false (security.facilitator_rejects_invalid_signature, critical). The demo target ships a deliberately broken facilitator route that approves everything:
settlens scan http://127.0.0.1:4402/secure \
--facilitator http://127.0.0.1:4402/facilitator-accept \
--active --authorized --authorization-ref DEMO-APPROVAL-003
Settlens x402 assessment 9fa4dcf9
Targets 1 · Active 1 · Settlement calls 0
127.0.0.1 GET http://127.0.0.1:4402/secure
26 pass · 1 fail · 0 warn · 0 skip
FAIL critical Facilitator /verify rejects an invalid signature
The facilitator reported isValid=true for an intentionally invalid, all-zero signature.
Test the facilitator directly, not only through the resource. The two can disagree, and the disagreement is exactly where money goes missing. Our guide to x402 facilitator security covers what /verify, /settle and /supported are each expected to do.
3. Weak payment-to-resource binding
Every x402 V2 challenge carries a resource object whose url says what is being paid for. When that URL does not match the URL the client requested, the client cannot tell what it is paying for. Common causes are mundane: one shared challenge for every route, a stale host name after a proxy or CDN change, or a query parameter dropped when the challenge is built. The effect is not mundane: a payment can be steered to, or reused for, a different resource than the one the agent asked for.
The check binding.resource_url compares resource.url with the requested URL, path and query included (the fragment is ignored), and fails at high severity when they differ. You can run it on a captured header with the free x402 payment request checker: paste the header and the URL you requested. On the server side, the matching control is to verify that the terms in a payment payload are the terms you issued for this resource (amount, recipient, asset and network) rather than accepting any valid signature that pays the right address. A passive scan cannot observe that server-side logic; it can only confirm that the challenge itself is bound correctly.
4. Recipient, asset and price drift
The payTo, asset and amount fields decide who gets paid, in which token and how much. If they change between two identical requests, something is wrong: a dynamic pricing bug, two backends behind a load balancer serving different configurations, a recipient rotation nobody reviewed, or tampering. If they drift away from the values you reviewed, funds can be silently redirected or customers overcharged.
Settlens catches drift in two ways. stability.payment_requirements requests the resource several times (two by default) and compares the resource, accepts and extensions it receives, ignoring known ephemeral fields such as a Solana recentBlockhash. A change raises a warning. Local policy checks then pin the values you approved: policy.recipient, policy.asset, policy.network, policy.scheme and policy.maximum_amount fail at high severity when the advertised terms leave the reviewed set. The demo's drift route alternates its price on every request:
settlens scan http://127.0.0.1:4402/drift
Settlens x402 assessment 56c93ec9
Targets 1 · Active 0 · Settlement calls 0
127.0.0.1 GET http://127.0.0.1:4402/drift
12 pass · 0 fail · 1 warn · 0 skip
WARN medium Payment requirements remain stable
Payment terms changed between identical unauthenticated requests.
A recipient that changes without a deploy is a red flag, not a feature. Our guide to detecting payment drift covers the stability check, policy pins and continuous monitoring in detail.
5. Malformed or non-standard challenges
Protocol drift breaks integrations without an obvious error. A server still emitting x402Version: 1 where clients expect 2, an amount written as "0.01" instead of an integer string of atomic units, a network written as base instead of the CAIP-2 identifier eip155:8453, a payTo that is not a 20-byte address on an EVM chain, or the same payment option listed twice. The official SDK schema catches some of these, such as a network that is not CAIP-2, but accepts others: a well-formed version-1 object, a decimal amount, a malformed payTo or a duplicate option. Hand-written clients may accept any of them and interpret them differently. To your users, every variant looks like a mysterious payment failure.
A passive scan checks that the PAYMENT-REQUIRED header is present and decodes as Base64 JSON, that the pinned official @x402/core 2.28.0 SDK accepts the object, that the V2 core shape is there, and, with its own rules on top of the SDK, that every payment option is well formed and that no option is duplicated. If you are still moving an implementation off version 1, the x402 V1 to V2 migration guide lists what changed on the wire. The demo's broken route answers 402 but forgets the header entirely:
settlens scan http://127.0.0.1:4402/missing-header
Settlens x402 assessment fe4514e2
Targets 1 · Active 0 · Settlement calls 0
127.0.0.1 GET http://127.0.0.1:4402/missing-header
4 pass · 1 fail · 0 warn · 1 skip
FAIL high PAYMENT-REQUIRED header is present
The response does not include PAYMENT-REQUIRED.
SKIP medium Payment requirements remain stable
Only one successful sample was available.
The stability check is skipped here because there was no decodable challenge to compare. A skip is not a pass: it means the prerequisite evidence was missing.
6. Transport weaknesses
Payment terms are only as trustworthy as the channel that carries them. Over plaintext HTTP, anyone on the network path can rewrite payTo or amount before the agent signs; transport.https warns about any non-loopback endpoint that is not served over HTTPS. A redirect moves the challenge away from the URL that was assessed, so Settlens never follows one: transport.redirect records the sanitized destination and warns instead. And a 402 response body should be small. Settlens stops reading at 1 MiB (transport.response_body_limit), because a very large challenge body wastes agent bandwidth and can mean the protected content is being sent before payment.
7. Long authorization windows and replay exposure
The maxTimeoutSeconds field is the maximum time the server allows for completing a payment. A long value invites long-lived authorizations, and the longer an authorization stays valid, the longer a leaked or intercepted one can be presented again. Settlens warns when any advertised value exceeds 3600 seconds (freshness.timeout_window, medium severity); keep it as short as settlement allows.
For the EVM exact scheme with its default EIP-3009 transfer method, each authorization is a signed message with a validAfter/validBefore window and a nonce, and the token contract refuses a nonce that was already used when the transfer settles. /verify commits no state, so until settlement nothing on-chain has consumed the nonce: a server that serves content after verification alone must make sure the same authorization is not accepted twice. Be precise about what a test can show here: Settlens checks the window of a captured authorization offline with verify-payment, and it checks that the advertised windows are bounded. It does not check whether a nonce was used on-chain and it does not test replay. Our EIP-3009 explainer covers the authorization format.
8. Facilitator coverage gaps
A resource can advertise a scheme and network that its facilitator does not support. The agent signs a perfectly valid payment that can never be verified or settled, and the failure surfaces as a refused purchase. Settlens reads the facilitator's GET /supported and checks its schema, its x402 V2 capability and its signer metadata, then cross-checks every advertised scheme and network against the supported kinds (facilitator.requirement_coverage) and the published signers (facilitator.signer_coverage). The demo includes a facilitator that only supports Base mainnet while the resource asks for Base Sepolia:
settlens scan http://127.0.0.1:4402/secure \
--facilitator http://127.0.0.1:4402/facilitator-empty
Settlens x402 assessment b92b9c27
Targets 1 · Active 0 · Settlement calls 0
127.0.0.1 GET http://127.0.0.1:4402/secure
22 pass · 1 fail · 0 warn · 0 skip
FAIL high Facilitator covers advertised payment requirements
1 advertised payment choice(s) are absent from facilitator capabilities.
The failure modes at a glance
The table is generated from the Settlens check catalog, the same source the CLI, the reports and the check reference use. Mode tells you what a check needs: offline checks run on the challenge content alone, passive checks need live unauthenticated requests, active checks need the authorized probe and facilitator checks read GET /supported.
| Failure mode | Checks | Mode · highest severity |
|---|---|---|
| 1. Fail-open resource | security.invalid_signature_fail_closedtransport.invalid_payment_status | active · critical |
| 2. Facilitator accepts a forged signature | security.facilitator_rejects_invalid_signaturefacilitator.verify_response_schema | active · critical |
| 3. Weak payment-to-resource binding | binding.resource_url | offline · high |
| 4. Recipient, asset and price drift | stability.payment_requirementspolicy.recipientpolicy.assetpolicy.maximum_amountpolicy.network | passive, offline · high |
| 5. Malformed challenge | transport.payment_required_headertransport.payment_required_encodingschema.official_x402_sdkschema.payment_requiredschema.payment_requirementsschema.duplicate_requirements | passive, offline · high |
| 6. Transport weaknesses | transport.httpstransport.redirecttransport.response_body_limit | passive · medium |
| 7. Long authorization windows | freshness.timeout_window | offline · medium |
| 8. Facilitator coverage gaps | facilitator.requirement_coveragefacilitator.signer_coverage | facilitator · high |
What these tests do not tell you
Each check reports observed behavior during the recorded window. A clean report does not prove that untested application code is free of vulnerabilities, that a real wallet signature will settle, that balances are sufficient or that a nonce is unused on-chain. Settlens does not call /settle, does not broadcast transactions and does not inspect the content you deliver after payment. It is evidence about the payment handshake, not a certification. The assessment methodology spells out what each status and severity means.
Within that boundary, every failure above is deterministic to test and cheap to run on every release. Start with the passive checks, add the facilitator, then run the authorized probe once you have the owner's approval. The practical testing checklist walks through the commands in order, and a pilot runs them against your own endpoints with a report your partners can verify.