Skip to content
LogoLogo

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.mjs

All 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.

ValuePreview behaviorPreview guidance
offPerforms vanilla x402 verification and settlement without a Guardian approval record.Use only to isolate preview behavior; it does not protect the resource with Guardian.
optionalRuns Guardian only when the payment payload carries navaApiKey.A payment without the payer key can settle without Guardian.
requiredRuns 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 402 response and does not run the route handler.
  • After approved verification and successful merchant settlement, it adds the X-PAYMENT-RESPONSE header, attaches settlement/Guardian summaries to request.x402, and lets the route return 200.
  • Invalid headers produce 400; unavailable quote, verification, or settlement dependencies produce a fail-closed error such as 502.

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.