---
title: "Test x402 payment terms before a release | Settlens"
description: "Reproduce five x402 V2 payment-policy checks with a free browser demo, curl and a small Node.js CI example. No account, wallet or payment key needed."
url: https://settlens.com/blog/check-x402-payment-policy
last_updated: 2026-10-15
published: 2026-10-15
author: "Alessandro Gagliardi"
---

# Test x402 payment terms before a release

> **In short:** Reproduce five synthetic x402 V2 cases through the public Settlens evaluator: approved terms, a higher price, another recipient, another resource and a missing recipient policy. Use the browser or curl, then run the same reviewed fixtures in CI without a wallet or private CLI.

A well-formed 402 can still ask for a price or recipient you did not approve. Keep the policy fixed, change one term, and inspect the decision yourself.

## See three decisions in 60 seconds

[Download the demonstration](https://settlens.com/assets/x402-policy-demo.mp4).

_Three still captures of real browser-checker responses, edited to 20 seconds each. Synthetic examples, recorded October 15, 2026. No audio, live merchant incident, signature or payment. The steps below are the text equivalent._

[Try the same three steps yourself](https://settlens.com/tools/x402-checker#guided-demo), or [download the 60-second video](https://settlens.com/assets/x402-policy-demo.mp4).

## Reproduce it in your browser

1. Open the [free checker](https://settlens.com/tools/x402-checker#checker), select **Load sample**, then **Check the request**. The expected decision is `pay` under the complete example policy.
2. Select **Price doubles**, then check again. The quote is now `20000` atomic units, above the unchanged `10000` ceiling. Expected decision: `do_not_pay`.
3. Select **Recipient changes**, then check again. The recipient differs from the unchanged allowlist. Expected decision: `do_not_pay`.

The policy belongs to the reviewer. Copying an untrusted quote into its own allowlist would defeat the check. A change may be intentional: review it before updating the policy.

_Expected results from the same evaluator used by the public checker_

| Synthetic case | Decision | Failed or warned checks |
| --- | --- | --- |
| Approved example | pay | None |
| Price doubles | do_not_pay | policy.maximum_amount, policy.acceptable_option |
| Recipient changes | do_not_pay | policy.recipient, policy.acceptable_option |
| Resource changes | do_not_pay | binding.resource_url |
| Recipient policy missing | review | None |

The approved fixture uses the specification’s Base Sepolia example (`eip155:84532`). These are illustrative terms, not a payment destination to fund. A `pay` decision means that an option meets the supplied policy and evaluated checks; it is not an instruction to transfer funds or a security guarantee.

## Run the same check with curl

This public call needs no token, account or access to the private Settlens CLI. It evaluates supplied data in memory; it never requests `api.example.com`. Do not include credentials, private keys, seed phrases or confidential payloads. See the [processing and rate-limit details](https://settlens.com/privacy#mcp-endpoint).

**Approved synthetic quote and a complete policy**

```shell
curl -s https://settlens.com/mcp \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"evaluate_payment_requirements","arguments":{"payment_required":{"x402Version":2,"error":"PAYMENT-SIGNATURE header is required","resource":{"url":"https://api.example.com/premium-data","description":"Access to premium market data","mimeType":"application/json"},"accepts":[{"scheme":"exact","network":"eip155:84532","amount":"10000","asset":"0x036CbD53842c5426634e7929541eC2318f3dCF7e","payTo":"0x209693Bc6afc0C5328bA36FaF03C514EF312287C","maxTimeoutSeconds":60,"extra":{"name":"USDC","version":"2"}}]},"resource_url":"https://api.example.com/premium-data","policy":{"pay_to":["0x209693Bc6afc0C5328bA36FaF03C514EF312287C"],"assets":["0x036CbD53842c5426634e7929541eC2318f3dCF7e"],"networks":["eip155:84532"],"schemes":["exact"],"max_amount":"10000"}}}}'
```

In `result.structuredContent`, inspect `decision`, `acceptable`, `reasons` and `checks`. Keep the policy unchanged and replace only `payment_required.accepts[0].amount` with `"20000"` to reproduce the price case.

## Keep reviewed cases in CI

Download the [five synthetic fixtures](https://settlens.com/assets/x402-policy-cases.json) and the [small Node.js runner](https://settlens.com/assets/x402-policy-regression.cjs). Review both before using them. With Node.js 20.19 or newer:

**Run the public example**

```shell
curl --fail --show-error -O https://settlens.com/assets/x402-policy-cases.json
curl --fail --show-error -O https://settlens.com/assets/x402-policy-regression.cjs
node x402-policy-regression.cjs x402-policy-cases.json
```

The runner exits nonzero on an unexpected decision, an HTTP failure, timeout or evaluator error. Its full source is below. The public evaluator is limited to 60 requests per minute per IP; each run makes five requests. Availability and shared CI egress limits can fail this job too.

**x402-policy-regression.cjs**

```javascript
// Public example: node x402-policy-regression.cjs x402-policy-cases.json
// Synthetic fixture checks, not a scan of a deployed merchant or a settlement test.
const { readFile } = require("node:fs/promises");

async function main() {
  const cases = JSON.parse(await readFile(process.argv[2] || "x402-policy-cases.json", "utf8"));
  if (!Array.isArray(cases) || !cases.length) throw new Error("Expected a non-empty array of reviewed cases.");
  for (const example of cases) {
    const response = await fetch("https://settlens.com/mcp", {
      method: "POST", redirect: "error", signal: AbortSignal.timeout(15000),
      headers: { "content-type": "application/json", accept: "application/json", "mcp-protocol-version": "2025-06-18" },
      body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/call", params: {
        name: "evaluate_payment_requirements", arguments: example.arguments,
      } }),
    });
    if (!response.ok) throw new Error(`Evaluator unavailable (HTTP ${response.status}).`);
    const message = await response.json();
    const result = message.result?.structuredContent;
    if (message.error || message.result?.isError || !result) throw new Error("Evaluator returned an error.");
    if (result.decision !== example.expected) throw new Error(`${example.name}: expected ${example.expected}, got ${result.decision}.`);
    console.log(`${example.name}: ${result.decision}`);
  }
}

main().catch((error) => { console.error(error.message); process.exitCode = 1; });
```

Commit reviewed fixtures and the runner to your own repository. For example, after checking out your code and selecting a supported Node.js version, add this step to your existing workflow:

**GitHub Actions step**

```yaml
- name: Check reviewed x402 fixtures
  run: node x402-policy-regression.cjs x402-policy-cases.json
```

## What this example establishes

This is a regression check for supplied challenges and policy decisions. The synthetic fixtures alone do not test a deployed merchant. For your release, capture a challenge from an endpoint you are authorized to test, review its terms independently, and decide which controlled changes must be refused. Never automatically refresh an approved reference from unreviewed live data.

It does not establish live HTTP behavior, signatures, balances, nonce use, on-chain settlement or overall payment safety. The exported decision JSON is unsigned. For a scoped endpoint scan, monitoring reference and private CLI/CI integration, [request a technical pilot](https://settlens.com/contact#pilot-request).

## Sources

1. [x402 V2 HTTP transport](https://github.com/x402-foundation/x402/blob/main/specs/transports-v2/http.md) — x402 Foundation
2. [x402 protocol specification, version 2](https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md) — x402 Foundation
