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-nestjs package provides a full-featured NestJS module with decorators, guards, and interceptors for payment-protecting your API routes.
Decorator-Based @Payment() and @Payer() decorators
Module System PrismModule.forRoot() configuration
Dependency Injection Works with NestJS DI system
Installation
npm install @1stdigital/prism-nestjs @nestjs/core @nestjs/common
bash yarn add @1stdigital/prism-nestjs @nestjs/core @nestjs/common
pnpm add @1stdigital/prism-nestjs @nestjs/core @nestjs/common
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
// app.module.ts
import { Module } from "@nestjs/common" ;
import { PrismModule } from "@1stdigital/prism-nestjs" ;
import { AppController } from "./app.controller" ;
@ Module ({
imports : [
PrismModule . forRoot ({
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" ,
},
},
}),
],
controllers : [ AppController ],
})
export class AppModule {}
// app.controller.ts
import { Controller , Get } from "@nestjs/common" ;
import { Payment , Payer } from "@1stdigital/prism-nestjs" ;
@ Controller ( "api" )
export class AppController {
@ Get ( "premium" )
@ Payment () // Requires payment!
getPremiumData (@ Payer () payer : string ) {
return {
message : "Premium content" ,
payer ,
};
}
}
Configuration
Module Options
PrismModule . forRoot ({
identifyToken : "your-project-identify-token" ,
baseUrl : "https://prism-gw.fd.xyz" ,
debug : true ,
routes : {
"/api/premium" : {
price : 0.01 ,
description : "Premium API" ,
},
"/api/data/*" : {
price : 0.005 ,
description : "Data API (wildcard)" ,
},
},
});
Decorators
@Payment() Decorator
import { Controller , Get } from "@nestjs/common" ;
import { Payment } from "@1stdigital/prism-nestjs" ;
@ Controller ( "api" )
export class DataController {
// Payment required
@ Get ( "premium" )
@ Payment ()
getPremium () {
return { data : "Premium content" };
}
// No payment required
@ Get ( "free" )
getFree () {
return { data : "Free content" };
}
}
@Payer() Decorator
import { Controller , Get } from "@nestjs/common" ;
import { Payment , Payer } from "@1stdigital/prism-nestjs" ;
@ Controller ( "api" )
export class DataController {
@ Get ( "premium" )
@ Payment ()
getPremium (@ Payer () payer : string ) {
// payer = '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'
return {
message : "Premium data" ,
payer ,
};
}
}
Accessing Payment Info
Request Context
import { Controller , Get , Req } from "@nestjs/common" ;
import { Payment } from "@1stdigital/prism-nestjs" ;
import { Request } from "express" ;
@ Controller ( "api" )
export class DataController {
@ Get ( "premium" )
@ Payment ()
getPremium (@ Req () request : Request ) {
// Access via request object
const payer = request . payer ;
const payment = request . locals ?. payment ;
return {
message : "Premium data" ,
payer ,
network : payment ?. network ,
};
}
}
Settlement Validation
NestJS uses an Interceptor to validate settlement:
// Internal implementation (automatic!)
@ Injectable ()
export class PrismSettlementInterceptor implements NestInterceptor {
async intercept ( context : ExecutionContext , next : CallHandler ) {
return next . handle (). pipe (
mergeMap ( async ( data ) => {
const response = context . switchToHttp (). getResponse ();
const settlementResult = await this . settlementCallback ( ... );
if ( ! settlementResult || ! settlementResult . success ) {
// ❌ Settlement failed
response . status ( 402 );
return {
error : 'Payment settlement failed' ,
details : settlementResult ?. errorReason
};
}
// ✅ Settlement succeeded
response . header ( 'X-PAYMENT-RESPONSE' , settlementResult . transaction );
return data ;
})
);
}
}
Testing
import { Test , TestingModule } from "@nestjs/testing" ;
import { INestApplication } from "@nestjs/common" ;
import * as request from "supertest" ;
import { AppModule } from "./app.module" ;
describe ( "PrismModule (e2e)" , () => {
let app : INestApplication ;
beforeAll ( async () => {
const moduleFixture : TestingModule = await Test . createTestingModule ({
imports : [ AppModule ],
}). compile ();
app = moduleFixture . createNestApplication ();
await app . init ();
});
afterAll ( async () => {
await app . close ();
});
it ( "returns 402 without payment" , () => {
return request ( app . getHttpServer ())
. get ( "/api/premium" )
. expect ( 402 )
. expect (( res ) => {
expect ( res . body . paymentRequired ). toBe ( true );
});
});
it ( "returns 200 with valid payment" , () => {
const payment = JSON . stringify ({
scheme : "eip3009" ,
signature : "0x" + "0" . repeat ( 130 ),
});
return request ( app . getHttpServer ())
. get ( "/api/premium" )
. set ( "X-PAYMENT" , payment )
. expect ( 200 )
. expect (( res ) => {
expect ( res . body . message ). toBe ( "Premium content" );
});
});
});
Production Deployment
// main.ts
import { NestFactory } from "@nestjs/core" ;
import { AppModule } from "./app.module" ;
async function bootstrap () {
const app = await NestFactory . create ( AppModule , {
logger : [ "error" , "warn" , "log" ],
});
// Enable CORS if needed
app . enableCors ({
origin : process . env . ALLOWED_ORIGINS ?. split ( "," ),
credentials : true ,
});
const port = parseInt ( process . env . PORT || "3000" );
await app . listen ( port );
console . log ( `NestJS + Prism running on port ${ port } ` );
}
bootstrap ();