Skip to main content
A complete example of a content server that gates premium articles behind payments.

Server

server.ts
import express from "express"
import { createMiddleware } from "@faremeter/middleware/express"
import { xSolanaSettlement } from "@faremeter/info/solana"

const app = express()

const articles = {
  "intro-to-x402": { title: "Introduction to x402", body: "The x402 protocol..." },
  "advanced-payments": { title: "Advanced Payment Patterns", body: "When building..." },
}

const contentPaywall = await createMiddleware({
  facilitatorURL: "https://facilitator.corbits.dev",
  accepts: [
    xSolanaSettlement({
      network: "devnet",
      payTo: process.env.MERCHANT_ADDRESS,
      asset: "USDC",
      amount: "50000",
    }),
  ],
})

app.get("/articles", (req, res) => {
  const previews = Object.entries(articles).map(([slug, article]) => ({
    slug,
    title: article.title,
  }))
  res.json(previews)
})

app.get("/articles/:slug", contentPaywall, (req, res) => {
  const article = articles[req.params.slug]
  if (!article) {
    res.status(404).json({ error: "Not found" })
    return
  }
  res.json(article)
})

app.listen(3000)

Client

client.ts
import "dotenv/config"
import { payer } from "@faremeter/rides"
import { getLogger } from "@faremeter/logs"

const logger = await getLogger(["client"])

await payer.addLocalWallet(process.env.PAYER_KEYPAIR_PATH)

const previews = await fetch("http://localhost:3000/articles")
logger.info(JSON.stringify(await previews.json()))

const article = await payer.fetch("http://localhost:3000/articles/intro-to-x402")
logger.info(JSON.stringify(await article.json()))

Running the example

# Terminal 1
MERCHANT_ADDRESS=7xKXwxRPMo2sUAT5... pnpm tsx server.ts

# Terminal 2
PAYER_KEYPAIR_PATH=~/.config/solana/id.json pnpm tsx client.ts
The article listing is free. Reading full articles requires payment. The middleware only applies to the /articles/:slug route.