Documentation Index Fetch the complete documentation index at: https://developers.fd.xyz/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The @1stdigital/prism-fastify package provides a Fastify plugin for payment-protecting your API routes using the x402 protocol. It leverages Fastify’s hook system for high-performance payment validation.
High Performance Fastify’s async hooks for minimal overhead
Plugin Architecture Standard Fastify plugin pattern
Type Safe Full TypeScript support with decorators
Installation
npm install @1stdigital/prism-fastify fastify
bash yarn add @1stdigital/prism-fastify fastify
pnpm add @1stdigital/prism-fastify fastify
The canonical SDK config is identifyToken (PRISM_IDENTIFY_TOKEN env var) — formerly apiKey / PRISM_API_KEY. SDKs accept the legacy names as fallback during the migration window; new code should use the canonical names.
Quick Start
import Fastify from "fastify" ;
import prismPlugin from "@1stdigital/prism-fastify" ;
const app = Fastify ({ logger : true });
// Register Prism plugin
await app . register ( prismPlugin , {
identifyToken : process . env . PRISM_IDENTIFY_TOKEN || "dev-key-123" ,
baseUrl : "https://prism-gw.fd.xyz" ,
routes : {
"/api/premium" : {
price : 0.01 , // $0.01 USD
description : "Premium API access" ,
},
},
});
// Protected route - payment verified automatically
app . get ( "/api/premium" , async ( request , reply ) => {
return {
message : "Premium content!" ,
payer : request . prismPayer , // Wallet address
};
});
await app . listen ({ port : 3000 });
Configuration
Plugin Options
interface PrismFastifyOptions {
identifyToken : string ; // Your Prism Project Identify Token
baseUrl ? : string ; // Gateway URL (optional)
debug ? : boolean ; // Enable debug logging
routes : Record < string , RoutePaymentConfig >;
}
interface RoutePaymentConfig {
price : number | string ; // Price in USD
description : string ; // Human-readable description
mimeType ? : string ; // Response MIME type
}
Route Protection
Plugin Registration
await app . register ( prismPlugin , {
identifyToken : "your-project-identify-token" ,
routes : {
"/api/premium" : {
price : 0.01 ,
description : "Premium API" ,
},
"/api/weather" : {
price : "$0.001" ,
description : "Weather data" ,
},
"/api/data/*" : {
price : 0.005 ,
description : "Data API (wildcard)" ,
},
},
});
Accessing Payment Info
Payment info is added to the request object:
app . get ( "/api/premium" , async ( request , reply ) => {
// Access payer wallet address
const payer = request . prismPayer ;
// '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'
// Access full payment object
const payment = request . prismPayment ;
/*
{
scheme: 'eip3009',
network: 'eth-sepolia',
asset: 'usdc',
amount: '10000',
signature: '0x...'
}
*/
return {
message : "Premium content" ,
payer ,
network : payment ?. network ,
};
});
Settlement Validation
Fastify uses the onSend hook to validate settlement before sending data:
// Internal implementation (automatic!)
fastify . addHook ( 'onSend' , async ( request , reply , payload ) => {
const settlementResult = await core . settlementCallback ( ... );
if ( ! settlementResult || ! settlementResult . success ) {
// ❌ Settlement failed - replace payload
reply . code ( 402 );
reply . header ( 'Content-Type' , 'application/json' );
return JSON . stringify ({
error : 'Payment settlement failed' ,
details : settlementResult ?. errorReason
});
}
// ✅ Settlement succeeded - add header and return payload
reply . header ( 'X-PAYMENT-RESPONSE' , settlementResult . transaction );
return payload ;
});
See Stablecoin Settlement for details.
TypeScript Support
Extend Fastify types for payment properties:
import "fastify" ;
declare module "fastify" {
interface FastifyRequest {
prismPayer ? : string ;
prismPayment ? : {
scheme : string ;
network : string ;
asset : string ;
amount : string ;
signature : string ;
};
}
}
// Now TypeScript knows about payment properties
app . get ( "/api/data" , async ( request , reply ) => {
const payer : string | undefined = request . prismPayer ; // Type-safe!
return { data : [ 1 , 2 , 3 ], payer };
});
Testing
import { test } from "tap" ;
import Fastify from "fastify" ;
import prismPlugin from "@1stdigital/prism-fastify" ;
test ( "payment required without header" , async ( t ) => {
const app = Fastify ();
await app . register ( prismPlugin , {
identifyToken : "test-key" ,
routes : {
"/api/premium" : { price : 0.01 , description : "Test" },
},
});
app . get ( "/api/premium" , async () => ({ message : "Premium" }));
const response = await app . inject ({
method : "GET" ,
url : "/api/premium" ,
});
t . equal ( response . statusCode , 402 );
t . ok ( response . json (). paymentRequired );
});
test ( "access granted with valid payment" , async ( t ) => {
const app = Fastify ();
await app . register ( prismPlugin , {
identifyToken : "test-key" ,
routes : {
"/api/premium" : { price : 0.01 , description : "Test" },
},
});
app . get ( "/api/premium" , async ( request ) => ({
message : "Premium" ,
payer : request . prismPayer ,
}));
const payment = JSON . stringify ({
scheme : "eip3009" ,
signature : "0x" + "0" . repeat ( 130 ),
});
const response = await app . inject ({
method : "GET" ,
url : "/api/premium" ,
headers : { "X-PAYMENT" : payment },
});
t . equal ( response . statusCode , 200 );
t . equal ( response . json (). message , "Premium" );
});
Production Deployment
import Fastify from "fastify" ;
import prismPlugin from "@1stdigital/prism-fastify" ;
const app = Fastify ({
logger : {
level : process . env . LOG_LEVEL || "info" ,
},
});
await app . register ( prismPlugin , {
identifyToken : process . env . PRISM_IDENTIFY_TOKEN ! ,
baseUrl : process . env . PRISM_BASE_URL ,
debug : process . env . NODE_ENV !== "production" ,
routes : {
"/api/premium" : {
price : 0.01 ,
description : "Premium API" ,
},
},
});
// Start server
await app . listen ({
port : parseInt ( process . env . PORT || "3000" ),
host : "0.0.0.0" ,
});