=============== LIBRARY RULES =============== From library maintainers: - toll-booth is payment-rail agnostic — configure Lightning (LND, CLN, Phoenixd), Cashu, NWC, or Stripe via payment providers. - Use the Express middleware for quickest integration: app.use(tollBooth(config)). - Pricing is defined per-route with support for metered, subscription, and one-time payment models. ### Quick Start Docker Compose Setup Source: https://github.com/forgesworn/toll-booth/blob/main/examples/valhalla-proxy/README.md Navigate to the example directory and copy the environment file. Then, start the Docker Compose services in detached mode. Ensure your Phoenixd password and ROOT_KEY are set in the .env file. ```bash cd examples/valhalla-proxy cp .env.example .env # add your Phoenixd password and ROOT_KEY docker compose up -d ``` -------------------------------- ### Deploy sats-for-laughs example Source: https://github.com/forgesworn/toll-booth/blob/main/README.md Commands to set up and start the sats-for-laughs example project. ```bash cd examples/sats-for-laughs cp .env.example .env # add your Phoenixd credentials docker compose up -d # or: MOCK=true npm start ``` -------------------------------- ### Run locally in mock mode Source: https://github.com/forgesworn/toll-booth/blob/main/examples/sats-for-laughs/README.md Install dependencies and start the server with mock payments enabled to test the L402 flow without a real Lightning node. ```bash npm install MOCK=true npm start ``` -------------------------------- ### Install toll-booth dependencies Source: https://github.com/forgesworn/toll-booth/blob/main/docs/guides/ollama-monetisation.md Initialize a new Node.js project and install the required packages. ```bash mkdir ollama-paid && cd ollama-paid npm init -y npm install express @forgesworn/toll-booth ``` -------------------------------- ### Minimal Express Example Source: https://github.com/forgesworn/toll-booth/blob/main/llms-small.txt A basic setup for an Express.js application using the Toll Booth middleware. It configures the adapter, backend, pricing, and upstream server. ```typescript import express from 'express' import { Booth } from '@forgesworn/toll-booth' import { phoenixdBackend } from '@forgesworn/toll-booth/backends/phoenixd' const app = express() const booth = new Booth({ adapter: 'express', backend: phoenixdBackend({ url: 'http://localhost:9740', password: process.env.PHOENIXD_PASSWORD! }), pricing: { '/api': 10 }, upstream: 'http://localhost:8080', }) app.get('/invoice-status/:paymentHash', booth.invoiceStatusHandler as any) app.post('/create-invoice', booth.createInvoiceHandler as any) app.use('/', booth.middleware as any) app.listen(3000) ``` -------------------------------- ### Start the services Source: https://github.com/forgesworn/toll-booth/blob/main/docs/guides/ollama-monetisation.md Commands to launch the Ollama server and the proxy server. ```bash ollama serve & node server.mjs ``` -------------------------------- ### Deno Server Example with Web Standard Adapter Source: https://context7.com/forgesworn/toll-booth/llms.txt Example of setting up a Deno server to handle requests using the Toll Booth's web standard adapter. Routes are defined for invoice status, creation, and general middleware. ```typescript // Deno example: Deno.serve({ port: 3000 }, async (req: Request) => { const url = new URL(req.url) if (url.pathname.startsWith('/invoice-status/')) return booth.invoiceStatusHandler(req) if (url.pathname === '/create-invoice' && req.method === 'POST') return booth.createInvoiceHandler(req) return booth.middleware(req) }) ``` -------------------------------- ### Bun Server Example with Web Standard Adapter Source: https://context7.com/forgesworn/toll-booth/llms.txt Example of setting up a Bun server to handle requests using the Toll Booth's web standard adapter. Routes are defined for invoice status, creation, and general middleware. ```typescript // Bun example: Bun.serve({ port: 3000, async fetch(req) { const url = new URL(req.url) if (url.pathname.startsWith('/invoice-status/')) return booth.invoiceStatusHandler(req) if (url.pathname === '/create-invoice' && req.method === 'POST') return booth.createInvoiceHandler(req) return booth.middleware(req) }, }) ``` -------------------------------- ### Deploy with Docker Compose Source: https://github.com/forgesworn/toll-booth/blob/main/examples/sats-for-laughs/README.md Prepare the environment configuration and start the services using Docker Compose. ```bash cp .env.example .env # set PHOENIXD_PASSWORD and ROOT_KEY docker compose up -d ``` -------------------------------- ### Install and Build toll-booth Source: https://github.com/forgesworn/toll-booth/blob/main/CONTRIBUTING.md Clone the repository, install dependencies, and build the project. Node 18+ is required. ```bash git clone https://github.com/forgesworn/toll-booth.git cd toll-booth npm install npm run build # tsc -> dist/ npm test # vitest (unit tests) npm run typecheck # tsc --noEmit ``` -------------------------------- ### Configure alternative Lightning backends Source: https://github.com/forgesworn/toll-booth/blob/main/docs/guides/ollama-monetisation.md Examples for connecting to LND, Core Lightning, or LNbits backends. ```js // LND import { lndBackend } from '@forgesworn/toll-booth/backends/lnd' const backend = lndBackend({ url: process.env.LND_REST_URL, macaroon: process.env.LND_MACAROON, }) // Core Lightning import { clnBackend } from '@forgesworn/toll-booth/backends/cln' const backend = clnBackend({ url: process.env.CLN_REST_URL, rune: process.env.CLN_RUNE, }) // LNbits import { lnbitsBackend } from '@forgesworn/toll-booth/backends/lnbits' const backend = lnbitsBackend({ url: process.env.LNBITS_URL, apiKey: process.env.LNBITS_API_KEY, }) ``` -------------------------------- ### Express (Lightning) Usage Example Source: https://github.com/forgesworn/toll-booth/blob/main/llms-full.txt Example demonstrating how to integrate Toll Booth with an Express.js application for handling Lightning payments. This setup includes pricing, upstream configuration, and free tier options. ```APIDOC ## Express (Lightning) Usage Example ```typescript import express from 'express' import { Booth } from '@forgesworn/toll-booth' import { phoenixdBackend } from '@forgesworn/toll-booth/backends/phoenixd' const app = express() app.use(express.json()) const booth = new Booth({ adapter: 'express', backend: phoenixdBackend({ url: process.env.PHOENIXD_URL!, password: process.env.PHOENIXD_PASSWORD!, }), pricing: { '/api': 10 }, upstream: 'http://localhost:8080', freeTier: { requestsPerDay: 5 }, // or { creditsPerDay: 100 } for usage-based creditTiers: [ { amountSats: 1000, creditSats: 1000, label: 'Standard' }, { amountSats: 5000, creditSats: 5500, label: 'Pro (10% bonus)' }, { amountSats: 10000, creditSats: 12000, label: 'Enterprise (20% bonus)' }, ], }) app.get('/invoice-status/:paymentHash', booth.invoiceStatusHandler as any) app.post('/create-invoice', booth.createInvoiceHandler as any) app.use('/', booth.middleware as any) app.listen(3000) ``` ``` -------------------------------- ### Web Standard (Deno, Bun, Cloudflare Workers) - Cashu-only Usage Example Source: https://github.com/forgesworn/toll-booth/blob/main/llms-full.txt Example for using Toll Booth in a Web Standard environment (Deno, Bun, Cloudflare Workers) for Cashu-only payments. This setup does not require a Lightning node and is suitable for serverless and edge deployments. ```APIDOC ## Web Standard (Deno, Bun, Cloudflare Workers) - Cashu-only No Lightning node required. Suitable for serverless and edge deployments. ```typescript import { Booth } from '@forgesworn/toll-booth' const booth = new Booth({ adapter: 'web-standard', redeemCashu: async (token, paymentHash) => { // Verify and redeem the ecash token with your Cashu mint. // Return the credited amount in satoshis. return amountRedeemed }, pricing: { '/api': 5 }, upstream: 'http://localhost:8080', }) // Bun example: Bun.serve({ port: 3000, async fetch(req) { const url = new URL(req.url) if (url.pathname === '/cashu-redeem') { return (booth.cashuRedeemHandler as any)(req) } if (url.pathname.startsWith('/invoice-status/')) { return (booth.invoiceStatusHandler as any)(req) } if (url.pathname === '/create-invoice') { return (booth.createInvoiceHandler as any)(req) } return (booth.middleware as any)(req) }, }) ``` ``` -------------------------------- ### Web Standard Usage Example (Cashu-only) Source: https://github.com/forgesworn/toll-booth/blob/main/llms-full.txt Example for serverless and edge deployments using Web Standard environments (Deno, Bun, Cloudflare Workers) for Cashu-only payments. No Lightning node is required. ```typescript import { Booth } from '@forgesworn/toll-booth' const booth = new Booth({ adapter: 'web-standard', redeemCashu: async (token, paymentHash) => { // Verify and redeem the ecash token with your Cashu mint. // Return the credited amount in satoshis. return amountRedeemed }, pricing: { '/api': 5 }, upstream: 'http://localhost:8080', }) // Bun example: Bun.serve({ port: 3000, async fetch(req) { const url = new URL(req.url) if (url.pathname === '/cashu-redeem') { return (booth.cashuRedeemHandler as any)(req) } if (url.pathname.startsWith('/invoice-status/')) { return (booth.invoiceStatusHandler as any)(req) } if (url.pathname === '/create-invoice') { return (booth.createInvoiceHandler as any)(req) } return (booth.middleware as any)(req) }, }) ``` -------------------------------- ### Hono Framework Integration Setup Source: https://context7.com/forgesworn/toll-booth/llms.txt Initialize the Hono integration for Toll Booth, using `createHonoTollBooth` and configuring the backend, storage, and root key. This setup is for Hono applications. ```typescript import { Hono } from 'hono' import { createTollBooth } from '@forgesworn/toll-booth' import { createHonoTollBooth, type TollBoothEnv } from '@forgesworn/toll-booth/hono' import { phoenixdBackend } from '@forgesworn/toll-booth/backends/phoenixd' import { sqliteStorage } from '@forgesworn/toll-booth/storage/sqlite' const storage = sqliteStorage({ path: './toll-booth.db' }) const rootKey = process.env.ROOT_KEY! const engine = createTollBooth({ backend: phoenixdBackend({ url: 'http://localhost:9740', password: process.env.PHOENIXD_PASSWORD! }), storage, pricing: { '/api': 10 }, upstream: 'http://localhost:8080', rootKey, freeTier: { requestsPerDay: 5 }, }) const tollBooth = createHonoTollBooth({ engine, trustProxy: true }) const app = new Hono() ``` -------------------------------- ### Docker Build Stages Source: https://github.com/forgesworn/toll-booth/blob/main/examples/valhalla-proxy/README.md The Dockerfile employs a multi-stage build. Stage 1 compiles toll-booth from source and creates a tarball. Stage 2 installs this tarball as a dependency and includes server.ts, ensuring the example uses the latest local toll-booth source. ```dockerfile # Stage 1 FROM node:18-alpine AS builder WORKDIR /app COPY . . RUN npm install --frozen-lockfile RUN npm run build RUN npm pack --pack-destination /app/dist # Stage 2 FROM node:18-alpine WORKDIR /app COPY --from=builder /app/dist/*.tgz . RUN npm install --frozen-lockfile *.tgz COPY server.ts . EXPOSE 3000 CMD ["node", "server.ts"] ``` -------------------------------- ### Configure Phoenixd Backend Source: https://github.com/forgesworn/toll-booth/blob/main/llms-full.txt Setup for the Phoenixd Lightning backend. ```typescript import { phoenixdBackend } from '@forgesworn/toll-booth/backends/phoenixd' interface PhoenixdConfig { url: string // e.g. "http://localhost:9740" password: string // Phoenixd auth password timeout?: number // request timeout ms (default: 30000) } phoenixdBackend(config: PhoenixdConfig): LightningBackend ``` -------------------------------- ### Example 402 Response Body with Booth and Auth Hint Source: https://github.com/forgesworn/toll-booth/blob/main/docs/superpowers/specs/2026-03-17-agent-friendly-402-bodies-design.md Illustrates a 402 response body including 'booth' and 'auth_hint' when 'serviceName' is configured. ```json { "message": "Payment required.", "booth": { "name": "Lightning Graph API", "description": "Network intelligence and channel suggestions" }, "auth_hint": "Pay the invoice, then send header — Authorization: L402 :", "l402": { "invoice": "lnbc...", "macaroon": "AgEB...", "payment_hash": "abcdef...", "amount_sats": 10 }, "tiers": {}, "credit_tiers": [ { "amountSats": 10, "creditSats": 10, "label": "Basic", "yields": "1 request" } ] } ``` -------------------------------- ### Install Toll Booth Source: https://github.com/forgesworn/toll-booth/blob/main/README.md Install the package via npm. ```bash npm install @forgesworn/toll-booth ``` -------------------------------- ### Configure Core Lightning (CLN) Backend Source: https://github.com/forgesworn/toll-booth/blob/main/llms-full.txt Setup for the Core Lightning backend using a rune token. ```typescript import { clnBackend } from '@forgesworn/toll-booth/backends/cln' interface ClnConfig { url: string // e.g. "https://localhost:3010" rune: string // rune token for authentication timeout?: number } clnBackend(config: ClnConfig): LightningBackend ``` -------------------------------- ### Run 402-mcp Client Source: https://github.com/forgesworn/toll-booth/blob/main/docs/guides/ai-agent-payments.md Start the 402-mcp client, which manages the wallet, pays invoices, and retries requests with L402 credentials. Requires NWC_URL environment variable. ```bash npx 402-mcp ``` -------------------------------- ### Test API requests Source: https://github.com/forgesworn/toll-booth/blob/main/docs/guides/ollama-monetisation.md Examples of using curl to make free requests, trigger a 402 challenge, and authenticate with a payment. ```bash curl http://localhost:3000/api/generate \ -d '{"model":"llama3.2","prompt":"Say hello in one sentence"}' ``` ```bash curl -i http://localhost:3000/api/generate \ -d '{"model":"llama3.2","prompt":"Why is the sky blue?"}' ``` ```bash curl http://localhost:3000/api/generate \ -H 'Authorization: L402 :' \ -d '{"model":"llama3.2","prompt":"Why is the sky blue?"}' ``` -------------------------------- ### Configure LNbits Backend Source: https://github.com/forgesworn/toll-booth/blob/main/llms-full.txt Setup for the LNbits Lightning backend, compatible with self-hosted or hosted instances. ```typescript import { lnbitsBackend } from '@forgesworn/toll-booth/backends/lnbits' interface LNbitsConfig { url: string // e.g. "https://legend.lnbits.com" apiKey: string // invoice/read API key from the LNbits wallet timeout?: number } lnbitsBackend(config: LNbitsConfig): LightningBackend ``` -------------------------------- ### Configure LND Backend Source: https://github.com/forgesworn/toll-booth/blob/main/llms-full.txt Setup for the LND Lightning backend. Either macaroon or macaroonPath must be provided. ```typescript import { lndBackend } from '@forgesworn/toll-booth/backends/lnd' interface LndConfig { url: string // e.g. "https://localhost:8080" macaroon?: string // admin macaroon as hex string macaroonPath?: string // path to admin.macaroon file (alternative to hex) timeout?: number // request timeout ms (default: 30000) } lndBackend(config: LndConfig): LightningBackend ``` -------------------------------- ### Configure NWC Backend Source: https://github.com/forgesworn/toll-booth/blob/main/llms-full.txt Setup for the Nostr Wallet Connect backend, which uses NIP-44 for encrypted communication. ```typescript import { nwcBackend } from '@forgesworn/toll-booth/backends/nwc' interface NwcConfig { /** NWC connection URI: nostr+walletconnect://pubkey?relay=wss://...&secret=... */ nwcUrl: string /** Reply timeout in ms (default: 60000) */ timeout?: number } nwcBackend(config: NwcConfig): LightningBackend ``` -------------------------------- ### Scaffold Toll Booth Project Source: https://github.com/forgesworn/toll-booth/blob/main/llms-small.txt Use the npx command to scaffold a new project with Toll Booth pre-configured. This is a convenient way to start a new project. ```bash npx @forgesworn/toll-booth init ``` -------------------------------- ### Enabling IETF Payment in Booth Configuration Source: https://github.com/forgesworn/toll-booth/blob/main/llms-full.txt Example of enabling the IETF payment rail within the Booth configuration. Requires specifying the realm and an optional description for the service. ```typescript const booth = new Booth({ adapter: 'express', backend: phoenixdBackend({ url: '...', password: '...' }), pricing: { '/api': 10 }, upstream: 'http://localhost:8080', ietfPayment: { realm: 'api.example.com', description: 'Pay-per-request API access', }, }) ``` -------------------------------- ### Install Toll-Booth dependency Source: https://github.com/forgesworn/toll-booth/blob/main/docs/guides/express-quickstart.md Install the required package via npm. ```bash npm install express @forgesworn/toll-booth ``` -------------------------------- ### Build and Test Commands Source: https://github.com/forgesworn/toll-booth/blob/main/GEMINI.md Standard npm scripts for building, testing, and type-checking the project. ```bash npm run build # tsc → dist/ npm test # vitest run npm run typecheck # tsc --noEmit ``` -------------------------------- ### Implement a mock backend for local testing Source: https://github.com/forgesworn/toll-booth/blob/main/docs/guides/ollama-monetisation.md Use in-memory storage and a mock backend to simulate payments without a real Lightning node. ```js import crypto from 'node:crypto' import { Booth, memoryStorage } from '@forgesworn/toll-booth' const storage = memoryStorage() const mockBackend = { async createInvoice(amountSats, memo) { const preimage = crypto.randomBytes(32) const paymentHash = crypto.createHash('sha256').update(preimage).digest('hex') const bolt11 = `lnbc${amountSats}n1mock${crypto.randomBytes(16).toString('hex')}` // Auto-settle after 1 second (simulates instant payment) setTimeout(() => storage.settleWithCredit(paymentHash, amountSats, preimage.toString('hex')), 1000) return { bolt11, paymentHash } }, async checkInvoice(paymentHash) { return { paid: storage.isSettled(paymentHash), preimage: storage.getSettlementSecret(paymentHash) } }, } const booth = new Booth({ adapter: 'express', backend: mockBackend, storage, upstream: 'http://localhost:11434', pricing: { '/api/generate': 50, '/api/chat': 100 }, freeTier: { requestsPerDay: 5 }, }) ``` -------------------------------- ### Run Toll-Booth Demo Source: https://github.com/forgesworn/toll-booth/blob/main/README.md This command spins up a fully functional L402-gated joke API on localhost with a mock Lightning backend and in-memory storage. It requires zero configuration. ```bash npx @forgesworn/toll-booth demo ``` -------------------------------- ### Install toll-booth Middleware Source: https://github.com/forgesworn/toll-booth/blob/main/llms.txt Install the toll-booth package using npm. This is the first step to integrate payment rails into your Node.js application. ```bash npm install @forgesworn/toll-booth ``` -------------------------------- ### Install toll-booth Package Source: https://github.com/forgesworn/toll-booth/blob/main/docs/vision.md Install the toll-booth npm package to integrate vending machine functionality into your HTTP API. This is the primary step for setting up the middleware. ```bash npm install @forgesworn/toll-booth ``` -------------------------------- ### Start Payment Polling Source: https://github.com/forgesworn/toll-booth/blob/main/examples/sats-for-laughs/public/index.html Starts polling a payment URL at a 3-second interval to check for payment status. If paid, it stops polling and potentially sets an L402 token. ```javascript function startPolling(paymentUrl, macaroon) { stopPolling(); pollTimer = setInterval(async function () { try { var res = await fetch(paymentUrl, { headers: { 'Accept': 'application/json' }, }); if (!res.ok) return; // silently retry var data = await res.json(); if (data.paid) { stopPolling(); // Build and store L402 token var suffix = data.preimage || data.token_suffix; if (suffix && macaroon) { setToken('L402 ' + macaroon + ':' + suffix); } // Set tier to match the bundle the user just purchased activeTier = purchasedTier; tierButtons.forEach(function (b) { b.classList.toggle('active', b.dataset.tier === purchasedTier); }); // Re-fetch the joke fetching = false; fetchJoke(activeTopic); } } catch (err) { // Silently retry on network errors } }, 3000); } ``` -------------------------------- ### Build and Test Commands Source: https://github.com/forgesworn/toll-booth/blob/main/AGENTS.md Standard npm scripts for building the project, running unit tests, and performing type checks. ```bash npm run build # tsc -> dist/ npm test # vitest run (unit tests) npm run typecheck # tsc --noEmit ``` -------------------------------- ### Configure Web Standard Adapter with LND Backend Source: https://context7.com/forgesworn/toll-booth/llms.txt Set up the Web Standard adapter for Deno, Bun, or Workers, integrating with the LND backend. Ensure environment variables for LND URL and macaroon are set. ```typescript import { Booth, memoryStorage } from '@forgesworn/toll-booth' import { lndBackend } from '@forgesworn/toll-booth/backends/lnd' const booth = new Booth({ adapter: 'web-standard', backend: lndBackend({ url: process.env.LND_REST_URL!, macaroon: process.env.LND_MACAROON!, }), storage: memoryStorage(), // Workers have no filesystem pricing: { '/api': 10 }, upstream: 'https://your-api.example.com', }) ``` -------------------------------- ### Kind 31402 Event Example Source: https://github.com/forgesworn/toll-booth/blob/main/docs/nip-402.md This is an example of a Kind 31402 event, used to announce a paid service. It includes details like service name, URLs, pricing, and capabilities. Clients should verify domain matching with NIP-05 for security. ```json { "kind": 31402, "pubkey": "", "created_at": 1710446400, "tags": [ ["d", "sats-for-laughs-jokes.trotters.dev"], ["name", "sats-for-laughs"], ["url", "https://jokes.trotters.dev"], ["url", "http://jokesexampleonionaddress.onion"], ["url", "https://satsforlaughs.jokes"], ["about", "Lightning-paid joke API with cracker, standard, and premium jokes across 6 topics"], ["pmi", "bitcoin-lightning-bolt11"], ["pmi", "bitcoin-cashu"], ["price", "cracker-joke", "5", "sats"], ["price", "standard-joke", "21", "sats"], ["price", "premium-joke", "42", "sats"], ["t", "jokes"], ["t", "humor"], ["t", "bitcoin"], ["t", "lightning"] ], "content": "{\"capabilities\":[{\"name\":\"cracker-joke\",\"description\":\"Bad puns and groaners\",\"endpoint\":\"/api/joke\"},{\"name\":\"standard-joke\",\"description\":\"Solid jokes across 6 topics\",\"endpoint\":\"/api/joke?tier=standard\"},{\"name\":\"premium-joke\",\"description\":\"Top-shelf comedy\",\"endpoint\":\"/api/joke?tier=premium\"}],\"version\":\"1.0.0\"}", "id": "", "sig": "" } ``` -------------------------------- ### Initialize Toll Booth with Event Hooks (Express) Source: https://github.com/forgesworn/toll-booth/blob/main/README.md Demonstrates initializing the Booth class with Express adapter, a backend, pricing, upstream, and custom event hooks for payment, request, and challenge events. ```typescript const booth = new Booth({ adapter: 'express', backend: phoenixdBackend({ url: '...', password: '...' }), pricing: { '/api': 10 }, upstream: 'http://localhost:8080', // Fired once per payment hash on first successful authentication onPayment: (event) => { console.log(`Received ${event.amountSats} sats via ${event.rail} (${event.paymentHash})`) // event: { timestamp, paymentHash, amountSats, currency, rail } }, // Fired on every authenticated or free-tier request onRequest: (event) => { console.log(`${event.endpoint} -${event.satsDeducted} sats, ${event.remainingBalance} remaining`) // event: { timestamp, endpoint, satsDeducted, remainingBalance, latencyMs, authenticated, currency, tier } }, // Fired when a 402 payment challenge is issued onChallenge: (event) => { console.log(`Challenged ${event.endpoint} for ${event.amountSats} sats`) // event: { timestamp, endpoint, amountSats } }, }) ``` -------------------------------- ### GET /invoice-status/:paymentHash Source: https://github.com/forgesworn/toll-booth/blob/main/llms.txt Retrieves the status of a specific payment invoice. ```APIDOC ## GET /invoice-status/:paymentHash ### Description Checks the status of an invoice using its payment hash. ### Method GET ### Endpoint /invoice-status/:paymentHash ### Parameters #### Path Parameters - **paymentHash** (string) - Required - The unique hash of the payment invoice. ``` -------------------------------- ### GET /invoice-status/:paymentHash Source: https://github.com/forgesworn/toll-booth/blob/main/llms-full.txt Endpoint to check the status of a specific payment invoice. ```APIDOC ## GET /invoice-status/:paymentHash ### Description Retrieves the current status of an invoice using its payment hash. ### Method GET ### Endpoint /invoice-status/:paymentHash ### Parameters #### Path Parameters - **paymentHash** (string) - Required - The unique hash identifier of the invoice. ``` -------------------------------- ### Integrate Storage with Booth Source: https://context7.com/forgesworn/toll-booth/llms.txt Use a custom storage backend when initializing the Booth. Ensure the adapter is also configured. ```typescript // Use with Booth: const booth = new Booth({ adapter: 'express', storage, // custom storage backend // ... other config }) ``` -------------------------------- ### Initial Joke Fetch Source: https://github.com/forgesworn/toll-booth/blob/main/examples/sats-for-laughs/public/index.html Initiates the fetching of a joke, likely as part of the initial page load or setup. ```javascript // -- Init -- fetchJoke(); ``` -------------------------------- ### Run Toll-Booth Demo Source: https://github.com/forgesworn/toll-booth/blob/main/docs/guides/ai-agent-payments.md Quickly spin up a fully functional L402-gated joke API with a mock Lightning backend for testing agent flows. ```bash npx @forgesworn/toll-booth demo ``` -------------------------------- ### Configure Memory Storage Backend Source: https://context7.com/forgesworn/toll-booth/llms.txt Initialize the memory storage backend, suitable for testing and ephemeral serverless deployments. Data is not persisted. ```typescript import { memoryStorage } from '@forgesworn/toll-booth/storage/memory' // Memory (for testing/serverless) const storage = memoryStorage() ``` -------------------------------- ### GET /invoice-status/:paymentHash Source: https://github.com/forgesworn/toll-booth/blob/main/examples/sats-for-laughs/README.md Checks the status of a Lightning invoice. Can return JSON or an HTML payment page. ```APIDOC ## GET /invoice-status/:paymentHash ### Description Checks the status of a Lightning invoice using its payment hash. This endpoint can return either a JSON object with the payment status or an HTML page for payment confirmation. ### Method GET ### Endpoint `/invoice-status/:paymentHash` ### Path Parameters - **paymentHash** (string) - Required - The unique identifier of the payment. ### Response #### Success Response (200) - **status** (string) - The status of the payment (e.g., "paid", "pending", "expired"). - **message** (string) - A message related to the payment status. #### Response Example (JSON) ```json { "status": "paid", "message": "Payment successful." } ``` #### Response Example (HTML) ```html Payment Status

Payment Successful!

Your payment has been confirmed.

``` ``` -------------------------------- ### Run Unit and Integration Tests Source: https://github.com/forgesworn/toll-booth/blob/main/CLAUDE.md Commands to run unit tests, or integration tests for specific backends by setting environment variables for connection details. ```bash npm test # unit tests only (no env vars needed) ``` ```bash LND_REST_URL=... LND_MACAROON=... npm test -- src/backends/lnd.integration.test.ts ``` ```bash CLN_REST_URL=... CLN_RUNE=... npm test -- src/backends/cln.integration.test.ts ``` ```bash PHOENIXD_URL=... PHOENIXD_PASSWORD=... npm test -- src/backends/phoenixd.integration.test.ts ``` -------------------------------- ### Filter Events via Relay Source: https://github.com/forgesworn/toll-booth/blob/main/docs/nip-402.md Examples of relay filter syntax used to reduce bandwidth by querying specific kinds and tags. ```json {"kinds": [31402], "#t": ["jokes"]} {"kinds": [31402], "#pmi": ["bitcoin-lightning-bolt11"]} {"kinds": [31402], "#pmi": ["bitcoin-cashu", "bitcoin-lightning-bolt11"]} ``` -------------------------------- ### Booth Configuration with Event Hooks Source: https://github.com/forgesworn/toll-booth/blob/main/llms.txt Configure the Booth facade with event hooks for observing payment lifecycle events. This example shows how to log payment, request, and challenge events. ```typescript const booth = new Booth({ adapter: 'express', backend: phoenixdBackend({ url: '...', password: '...' }), pricing: { '/api': 10 }, upstream: 'http://localhost:8080', onPayment: (event) => { console.log(`Payment: ${event.amountSats} sats via ${event.rail} (${event.paymentHash})`) }, onRequest: (event) => { console.log(`${event.endpoint} -${event.satsDeducted} sats, ${event.remainingBalance} remaining`) }, onChallenge: (event) => { console.log(`Challenged ${event.endpoint} for ${event.amountSats} sats`) }, }) ``` -------------------------------- ### Project Directory Structure Source: https://github.com/forgesworn/toll-booth/blob/main/GEMINI.md Overview of the source code organization, including core logic, storage backends, and framework adapters. ```text src/ index.ts # Public API exports types.ts # LightningBackend, BoothConfig, Invoice, CreditTier, events booth.ts # Booth class: facade that wires engine + adapters + storage macaroon.ts # Macaroon minting, verification, caveat parsing free-tier.ts # Per-IP daily allowance tracking (in-memory) payment-page.ts # Self-service HTML payment UI (QR, tier selector, wallet adapters) stats.ts # StatsCollector: in-memory usage analytics cli.ts # CLI entry point (demo, init) init.ts # Interactive project scaffolder core/ toll-booth.ts # TollBoothEngine: framework-agnostic L402 payment flow payment-rail.ts # PaymentRail interface and pricing normalisation l402-rail.ts # L402 Lightning + macaroon payment rail x402-rail.ts # x402 on-chain stablecoin payment rail xcashu-rail.ts # xcashu (NUT-24) direct-header payment rail ietf-payment.ts # IETF Payment auth rail (draft-ryan-httpauth-payment-01) ietf-session.ts # IETF Payment session intent rail create-invoice.ts # POST /create-invoice handler (tier support) invoice-status.ts # GET /invoice-status/:paymentHash handler nwc-pay.ts # NWC (Nostr Wallet Connect) payment handler cashu-redeem.ts # Cashu token redemption with lease/recovery logic storage/ interface.ts # StorageBackend interface (credits, invoices, claims) sqlite.ts # SQLite implementation (better-sqlite3, WAL mode) memory.ts # In-memory implementation (tests, ephemeral use) adapters/ express.ts # Express 5 middleware + handlers web-standard.ts # Web Standard (Request/Response) handlers (Deno, Bun, Workers) hono.ts # Hono middleware + payment route sub-app backends/ phoenixd.ts # Phoenixd Lightning backend (HTTP API) lnd.ts # LND Lightning backend (REST API) cln.ts # Core Lightning backend (clnrest API) lnbits.ts # LNbits Lightning backend (REST API) nwc.ts # Nostr Wallet Connect (NIP-47) backend conformance.ts # Shared backend conformance test factory e2e/ # End-to-end integration tests examples/ sats-for-laughs/ # Complete joke API deployment (live at jokes.trotters.dev) valhalla-proxy/ # Docker Compose reference (Express + Phoenixd) ```