@bsv/payment-express-middleware
Express.js middleware for HTTP 402 Payment Required micropayment gating. Builds on top of BRC-103 auth middleware to monetize API endpoints by requiring BSV satoshi payments derived using BRC-29 key derivation.
Install
bash
npm install @bsv/payment-express-middleware @bsv/auth-express-middleware @bsv/simpleQuick start
typescript
import express from 'express'
import bodyParser from 'body-parser'
import { createAuthMiddleware } from '@bsv/auth-express-middleware'
import { createPaymentMiddleware } from '@bsv/payment-express-middleware'
import { ServerWallet } from '@bsv/simple/server'
const wallet = await ServerWallet.create({
privateKey: process.env.SERVER_PRIVATE_KEY!,
network: 'main',
storageUrl: 'https://store-us-1.bsvb.tech'
})
const authMiddleware = createAuthMiddleware({ wallet })
const paymentMiddleware = createPaymentMiddleware({
wallet,
calculateRequestPrice: async (req) => {
return 50 // 50 satoshis per request
}
})
const app = express()
app.use(bodyParser.json())
app.use(authMiddleware)
app.use(paymentMiddleware)
app.post('/somePaidEndpoint', (req, res) => {
res.json({
message: 'Payment received, request authorized',
amount: req.payment.satoshisPaid
})
})
app.listen(3000)What it provides
- createPaymentMiddleware — Express middleware factory for 402 payment gating
- HTTP 402 Payment Required — Responds with 402 when payment needed
- BRC-29 derivation — Payment output derived from
derivationPrefix+derivationSuffix - Nonce reuse prevention — Each 402 response includes fresh
derivationPrefix - Wallet integration — Validates payment using
wallet.internalizeAction()method - Dynamic pricing —
calculateRequestPricefunction per endpoint/request - Payment object —
req.paymentwithsatoshisPaid,accepted,txfields - Response headers —
x-bsv-payment-*headers with invoice details
Common patterns
Basic payment gating
typescript
import express from 'express'
import { createPaymentMiddleware } from '@bsv/payment-express-middleware'
const paymentMiddleware = createPaymentMiddleware({
wallet,
calculateRequestPrice: async (req) => {
return 100 // 100 satoshis per request
}
})
const app = express()
app.use(authMiddleware)
app.use(paymentMiddleware)
app.get('/content', (req, res) => {
if (req.payment.accepted) {
res.json({ content: 'Premium content here' })
}
})Dynamic pricing
typescript
const paymentMiddleware = createPaymentMiddleware({
wallet,
calculateRequestPrice: async (req) => {
if (req.path === '/premium') return 500 // premium costs 500 sats
if (req.path === '/free') return 0 // free content
return 100 // default 100 sats
}
})Chained with auth middleware
typescript
const app = express()
app.use(bodyParser.json())
// Auth first
app.use(createAuthMiddleware({ wallet }))
// Then payment
app.use(createPaymentMiddleware({
wallet,
calculateRequestPrice: async (req) => 100
}))
// Routes now require both auth AND payment
app.post('/api/paid-endpoint', (req, res) => {
// req.auth.identityKey is authenticated
// req.payment.satoshisPaid was received
res.json({ result: 'success' })
})Key concepts
- 402 Payment Required — HTTP status code signaling payment is needed
- Micropayments — Small satoshi amounts (1-1000 typical) for API access
- BRC-29 derivation — Payment output derived from
derivationPrefix+derivationSuffix - Nonce reuse prevention — Each 402 response includes fresh
derivationPrefix - Wallet integration — Server validates payment using
wallet.internalizeAction()method - Auth prerequisite — Assumes
req.auth.identityKeyset by auth middleware - Transaction format — Client sends BSV transaction as base64 in header
When to use this
- Monetizing API endpoints with micropayments
- Creating pay-per-request services
- Building content that requires payment to access
- Implementing dynamic pricing based on request
- Creating subscription-like services with per-use charges
When NOT to use this
- Traditional subscription billing — use Stripe or similar
- Complex payment workflows — use a payment processor
- Free public APIs — skip this middleware
- Offline payment tracking — use a database instead
Spec conformance
- HTTP 402 Payment Required — Uses standard 402 status code
- BRC-29 (Payment Derivation): Uses BRC-29 nonce/derivation model for payment address generation
- BRC-100 (Wallet interface): Requires wallet implementing
internalizeAction()method - BRC-104 — Builds on top of BRC-104 HTTP auth transport
Common pitfalls
- Auth middleware must run first — Payment middleware expects
req.auth.identityKey - Wallet must implement
internalizeAction()— Critical method for validating and accepting payments - Nonce verification required — Middleware verifies
derivationPrefixmatches server's private key - Transaction format strict — Client sends
x-bsv-paymentheader as JSON with transaction - Replay protection essential — Wallet should reject duplicate
derivationPrefixvalues - HTTPS recommended — No encryption; use TLS to protect payment transactions
- Pricing consistency — If
calculateRequestPriceis async, ensure no timing side-effects
Related packages
- @bsv/402-pay — Client-side handler for paying 402 responses
- @bsv/auth-express-middleware — Required for authentication before payment
- @bsv/sdk — Nonce creation/verification, wallet interface, transaction utilities