Add x402station preflight example#1633
Conversation
Greptile SummaryAdds a new
Confidence Score: 4/5Safe to merge as a self-contained example; the default trial endpoint works end-to-end, and the two design concerns only surface on the production upgrade path. The example functions correctly with its default trial URL and Hardhat wallet. The two concerns — inconsistent URL parameterization in packages/examples/src/payments/x402station-preflight/index.ts — the Important Files Changed
Sequence DiagramsequenceDiagram
participant C as Caller
participant A as x402station-preflight-agent
participant X as x402station.io (preflight API)
participant T as Target Paid Endpoint
C->>A: POST /entrypoints/delegate-with-preflight/invoke
A->>A: buildEntrypointInvokeUrl(targetUrl, endpoint)
A->>X: "POST /api/v1/preflight-trial { url: targetUrl } via fetchWithPayment"
X-->>A: "PreflightVerdict { ok, warnings, recommended_action, ... }"
alt "shouldBlockPayment(verdict) == true"
A-->>C: "{ blocked: true, preflight: summary }"
else preflight allows target
A->>T: "POST /entrypoints/{endpoint}/invoke via fetchWithPayment"
Note over A,T: Lucid payment policies enforced here (budget, rate-limit, allowedRecipients)
T-->>A: Response (may include 402 auto-pay)
A-->>C: "{ blocked: false, preflight: summary, result: ... }"
end
Prompt To Fix All With AIFix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
packages/examples/src/payments/x402station-preflight/index.ts:52-56
**`runPreflight` silently captures the preflight URL from outer scope**
`runPreflight` accepts `fetchImpl` as a parameter to allow swapping between plain `fetch` and `fetchWithPayment`, but always reads `DEFAULT_PREFLIGHT_URL` directly from the module-level constant. This means the caller has no way to pass a different preflight URL without mutating the env var, and the two "configurable" dimensions (how vs. where) follow inconsistent patterns. A future caller passing a custom fetch would still be silently routed to whichever URL the env happened to set at module load time. Consider adding a `preflightUrl` parameter, or switching to a single config object.
### Issue 2 of 2
packages/examples/src/payments/x402station-preflight/index.ts:155-158
**Production preflight via `fetchWithPayment` will be blocked by existing payment policies**
`runPreflight` passes `paymentContext.fetchWithPayment` as the fetch implementation. The shared `payment-policies.json` used by this example has `"allowedRecipients": ["http://localhost:3001"]`. If a user follows the README's upgrade path and sets `X402STATION_PREFLIGHT_URL=https://x402station.io/api/v1/preflight` (the production endpoint that requires payment), `fetchWithPayment` would try to settle a 402 for `x402station.io` — but that host is not in `allowedRecipients`, so Lucid's own policy layer would block it. The trial endpoint (default) is free and unaffected, but the README should explicitly call out the need to add x402station.io to `allowedRecipients` before switching to the paid preflight tier.
Reviews (1): Last reviewed commit: "Add x402station preflight example" | Re-trigger Greptile |
| const response = await fetchImpl(DEFAULT_PREFLIGHT_URL, { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| body: JSON.stringify({ url: targetUrl }), | ||
| }); |
There was a problem hiding this comment.
runPreflight silently captures the preflight URL from outer scope
runPreflight accepts fetchImpl as a parameter to allow swapping between plain fetch and fetchWithPayment, but always reads DEFAULT_PREFLIGHT_URL directly from the module-level constant. This means the caller has no way to pass a different preflight URL without mutating the env var, and the two "configurable" dimensions (how vs. where) follow inconsistent patterns. A future caller passing a custom fetch would still be silently routed to whichever URL the env happened to set at module load time. Consider adding a preflightUrl parameter, or switching to a single config object.
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/examples/src/payments/x402station-preflight/index.ts
Line: 52-56
Comment:
**`runPreflight` silently captures the preflight URL from outer scope**
`runPreflight` accepts `fetchImpl` as a parameter to allow swapping between plain `fetch` and `fetchWithPayment`, but always reads `DEFAULT_PREFLIGHT_URL` directly from the module-level constant. This means the caller has no way to pass a different preflight URL without mutating the env var, and the two "configurable" dimensions (how vs. where) follow inconsistent patterns. A future caller passing a custom fetch would still be silently routed to whichever URL the env happened to set at module load time. Consider adding a `preflightUrl` parameter, or switching to a single config object.
How can I resolve this? If you propose a fix, please make it concise.| const preflight = await runPreflight( | ||
| paymentContext.fetchWithPayment, | ||
| target | ||
| ); |
There was a problem hiding this comment.
Production preflight via
fetchWithPayment will be blocked by existing payment policies
runPreflight passes paymentContext.fetchWithPayment as the fetch implementation. The shared payment-policies.json used by this example has "allowedRecipients": ["http://localhost:3001"]. If a user follows the README's upgrade path and sets X402STATION_PREFLIGHT_URL=https://x402station.io/api/v1/preflight (the production endpoint that requires payment), fetchWithPayment would try to settle a 402 for x402station.io — but that host is not in allowedRecipients, so Lucid's own policy layer would block it. The trial endpoint (default) is free and unaffected, but the README should explicitly call out the need to add x402station.io to allowedRecipients before switching to the paid preflight tier.
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/examples/src/payments/x402station-preflight/index.ts
Line: 155-158
Comment:
**Production preflight via `fetchWithPayment` will be blocked by existing payment policies**
`runPreflight` passes `paymentContext.fetchWithPayment` as the fetch implementation. The shared `payment-policies.json` used by this example has `"allowedRecipients": ["http://localhost:3001"]`. If a user follows the README's upgrade path and sets `X402STATION_PREFLIGHT_URL=https://x402station.io/api/v1/preflight` (the production endpoint that requires payment), `fetchWithPayment` would try to settle a 402 for `x402station.io` — but that host is not in `allowedRecipients`, so Lucid's own policy layer would block it. The trial endpoint (default) is free and unaffected, but the README should explicitly call out the need to add x402station.io to `allowedRecipients` before switching to the paid preflight tier.
How can I resolve this? If you propose a fix, please make it concise.
Summary
payments/x402station-preflightexamplefetchWithPaymentsecondValidation
bun run type-checkinpackages/examplesbun run lintinpackages/examples(existing warnings only)bun run format:checkinpackages/examples