Protect a resource server
Developer Preview
This path uses the Nava facilitator preview. The Fastify adapter is exported at
@navalabs/x402-guardian/fastify:
import Fastify from 'fastify';
import { paymentGate } from '@navalabs/x402-guardian/fastify';The complete placeholder example is in
examples/gateway-fastify/server.mjs.
Read the tested Fastify server
// [!include ../../../../examples/gateway-fastify/server.mjs:gateway-fastify]Configure the preview
Keep values in environment variables. The checked-in example requires a facilitator URL, merchant address, and price, and accepts a trusted-caller key when the preview facilitator protects its endpoints.
$env:FACILITATOR_URL = 'https://facilitator.example.invalid'
$env:FACILITATOR_API_KEY = '<facilitator-key>'
$env:MERCHANT_ADDRESS = '0x1111111111111111111111111111111111111111'
$env:NETWORK = 'base-sepolia'
$env:RESOURCE_PRICE = '40000'
$env:GUARDIAN_MODE = 'required'
$env:GUARDIAN_FEE = 'false'
node examples/gateway-fastify/server.mjsAll values above are placeholders. Do not place payer keys, facilitator wallet keys, or other secrets in source control.
GUARDIAN_MODE controls the facilitator process. The example mirrors the value
into the resource's Guardian advertisement; changing it only on the resource
server does not reconfigure a remote facilitator, so keep both sides aligned.
| Value | Preview behavior | Preview guidance |
|---|---|---|
off | Performs vanilla x402 verification and settlement without a Guardian approval record. | Use only to isolate preview behavior; it does not protect the resource with Guardian. |
optional | Runs Guardian only when the payment payload carries navaApiKey. | A payment without the payer key can settle without Guardian. |
required | Runs Guardian for every payment and requires a terminal approval before merchant settlement. | Recommended fail-closed setting only in a controlled preview environment. |
The resource gate's guardian.advertise setting must match the intended
facilitator mode. advertise: "required" prevents the Fastify route from
continuing on a structurally valid response that lacks an approved Guardian
status.
Build a deterministic quote
price can be a string, quote object, or callback. x402 replays the request with
a signed payment, so the callback must return the same quote for the same request
context. For example:
const gate = paymentGate({
network: process.env.NETWORK,
payTo: process.env.MERCHANT_ADDRESS,
facilitatorUrl: process.env.FACILITATOR_URL,
facilitatorKey: process.env.FACILITATOR_API_KEY,
resource: 'https://resource.example.invalid/premium',
ctx: () => ({ plan: 'standard' }),
price: ({ plan }) => ({
amount: plan === 'standard' ? process.env.RESOURCE_PRICE : '0',
description: 'One placeholder premium response',
}),
guardian: { advertise: 'required', fee: false },
});Do not calculate a usage-dependent amount after serving the resource. The Developer Preview has no usage-metered quote ID to bind such a later price.
Protect one route
Use the gate as a preHandler:
server.get('/premium', { preHandler: gate }, async (request) => ({
message: 'Placeholder protected response',
settlement: request.x402?.settlement,
guardian: request.x402?.guardian,
}));The adapter handles both response paths:
- Without a valid payment, or after a policy rejection, it sends a
402response and does not run the route handler. - After approved verification and successful merchant settlement, it adds the
X-PAYMENT-RESPONSEheader, attaches settlement/Guardian summaries torequest.x402, and lets the route return200. - Invalid headers produce
400; unavailable quote, verification, or settlement dependencies produce a fail-closed error such as502.
The facilitator consumes a Guardian approval before it broadcasts the merchant
transfer. A failed /settle call or a 502 from the resource gate therefore
does not leave an approval that can safely be retried. Re-run /verify for the
exact payment before another settlement attempt. If a verification fee was
already settled, the payer may also need to sign a fresh fee authorization.
Never blindly retry /settle after an uncertain or failed response.
Verification fees
When Guardian applies and guardian.fee is enabled, the initial 402 can contain
two requirements: the merchant payment in accepts and a separate
verification payment. The verification fee is settled during /verify on
either terminal Guardian outcome—approved or rejected—because the check
completed. It is not settled on timeout/error. The merchant payment goes to
/settle only after approval.
If a fee is quoted, the facilitator requires the bundled fee authorization, re-fetches the authoritative quote, binds payee/amount/asset exactly, and denies access if an approved fee fails to settle.