All unit tests pass, but integration tests still fail. Most x402 pitfalls occur at boundaries: 402 response headers sent but unreadable by the browser (missing CORS exposed headers), signatures computed correctly but rejected by the facilitator (nonce reuse / incorrect amount unit), funds received but reconciliation fails (wrong reconciliation source). This tool follows the five-step flow “free discovery → 402 → signature payment → data retrieval → on-chain reconciliation”, generating both a runnable integration testing script and a checklist for each step listing “expected outcome + common failure causes”.
—
—
| Phenomenon | Root Cause | How to Fix |
|---|---|---|
| Packet capture shows 402, but JS cannot read the header | Not Set Access-Control-Expose-Headers | Add custom headers such as payment-required to the exposed list |
| facilitator reports invalid nonce | Nonce reuse, or mismatch between signature domain and chain ID | | Generate a fresh random nonce each time; verify the domain's chainId. |
| facilitator reports amount mismatch | Incorrect amount unit (USDC uses 6 decimal places) | 0.01 USDC = 10000, not 1e16 | |
| Buyer can pay even without gas (this is correct) | EIP-3009 transferWithAuthorization Gas paid by facilitator | | Buyers only need to hold USDC |
| Funds arrived but reconciliation failed | Reconcile using the platform’s own records, not on-chain events | Only on-chain Transfer events are accepted; tolerance ±$0.02 | |
| Funds Never Arrive | Below minimum settlement threshold / Settlement date not yet reached | Aggregated by calendar month; settled on the 10th of the following month; minimum $50 per settlement | |
| Health check marked endpoint as dead | MCP endpoint returns SSE, client fails to parse as JSON | Judged solely by HTTP status code: both 200 and 402 are considered alive |
1 · 402 sent, but browser cannot read itCustom response headers are visible in packet captures but inaccessible in JS—must be explicitly added to the exposed list. Access-Control-Expose-Headers, otherwise the browser blocks it directly per security policies.
2 · Health check false negative.Using a JSON client to probe an MCP endpoint returning SSE always results in “dead”; correct behavior is to inspect only the status code—both 200 and 402 indicate liveness.
3 · Amount unit.USDC has 6 decimal places; 0.01 must be written as 10000; writing it as 1e16 will cause immediate rejection by the facilitator, and the error message won’t indicate the unit mismatch.
4 · Reconciliation only recognizes on-chain data.The platform’s own records may be inaccurate due to process restarts or overwritten stale in-memory values; reconciliation must rely on on-chain Transfer events.