<>ts-stack
Get StartedArchitecturePackagesSpecsGuides
⌘K
Reference
Home
Get StartedOverviewInstallChoose your stackKey concepts
ArchitectureOverviewStack layersBEEF (BRC-62)BRC-100 Wallet InterfaceIdentity & AuthConformance pipeline
PackagesOverviewSDKWalletNetworkOverlaysMessagingMiddleware
@bsv/auth-express-middleware@bsv/payment-express-middleware@bsv/402-pay
Helpers
InfrastructureOverviewmessage-box-serveroverlay-serveruhrp-server-basicuhrp-server-cloud-bucketwabwallet-infrachaintracks-server
SpecsOverviewBRC-100 Wallet InterfaceOverlay HTTPMessage-box HTTPAuthsocket (WebSocket)BRC-31 Auth HandshakeBRC-29 Peer PaymentBRC-121 / HTTP 402ARC BroadcastMerkle ServiceStorage AdapterGASP SyncUHRPAir-Gap Optical (BRC-141)
ConformanceOverviewVector catalogTS runnerContributing vectors
GuidesOverviewIdentity, DIDs and credentialsIdentity migrationBuild a wallet-aware appRun an overlay nodePeer-to-peer messagingHTTP 402 payments
ReferenceOverviewBRC indexRepository health
AboutVersioningContributingDoc agentDocumentation sources
npmhttps://www.npmjs.com/package/@bsv/402-payv0.3.3betaAPI reference (TypeDoc) ↗
Loading…
Edit this page on GitHub
© 2026 BSV Blockchain. ts-stack is open-source.
GitHubContributingVersioning

@bsv/402-pay

This source candidate declares SDK peer ^2.1.6 || ^3.0.0. SDK3 remains a coordinated proposal; see the qualification and migration limits before adopting it.

BRC-121 HTTP 402 Payment Required handler for client and server. Client-side: auto-pays 402 responses. Server-side: middleware/validation for accepting BSV micropayments over HTTP.

Install

bash
npm install @bsv/402-pay

Quick start

typescript
// ===== SERVER SIDE =====
import express from 'express'
import { createPaymentMiddleware } from '@bsv/402-pay/server'

const app = express()

app.use(
  '/articles/:slug',
  createPaymentMiddleware({
    wallet,
    replayStore: sharedAtomicReplayStore,
    calculatePrice: path => {
      if (path.includes('/premium/')) return 1000
      return undefined
    }
  })
)

app.get('/articles/:slug', (req, res) => {
  if (req.payment) {
    res.json({ article: 'Paid content here', paidBy: req.payment.senderIdentityKey })
  } else {
    res.json({ article: 'Free content' })
  }
})

// ===== CLIENT SIDE =====
import { create402Fetch } from '@bsv/402-pay/client'

const fetch402 = create402Fetch({
  wallet,
  cacheTimeoutMs: 30 * 60 * 1000
})

const response = await fetch402('https://example.com/articles/foo')
const article = await response.json()
console.log(article)

What it provides

Server

  • createPaymentMiddleware — Express middleware for automatic 402 handling
  • validatePayment — Validate incoming payment headers
  • send402 — Send 402 response with payment request headers
  • InMemoryPaymentReplayStore — Bounded process-local replay store used by default

Client

  • create402Fetch — Fetch wrapper that auto-handles 402 responses
  • constructPaymentHeaders — Build payment headers manually
  • clearCache — Clear cached paid content

Types & Constants

  • PaymentMiddlewareOptions — Middleware configuration
  • PaymentReplayStore — Atomic transaction-ID claim boundary; use a shared durable implementation across processes
  • PaymentResult — Parsed payment with satoshisPaid, senderIdentityKey, and txid
  • PaymentHeaders — Five required headers (BEEF, SENDER, NONCE, TIME, VOUT)
  • HEADERS — Header names enum
  • BRC29_PROTOCOL_ID — Protocol ID for payment key derivation

Common patterns

Server low-level API

typescript
import express from 'express'
import { validatePayment, send402 } from '@bsv/402-pay/server'

const app = express()

app.get('/premium', async (req, res) => {
  const price = 100
  const result = await validatePayment(req, wallet, price)
  if (!result || !result.accepted) {
    send402(res, serverIdentityKey, price)
    return
  }
  res.json({
    content: 'Premium stuff',
    tx: result.txid,
    paidBy: result.senderIdentityKey
  })
})

Client manual headers

typescript
import { constructPaymentHeaders } from '@bsv/402-pay/client'

const headers = await constructPaymentHeaders(
  wallet,
  'https://example.com/articles/foo',
  100, // 100 sats
  serverPublicKey
)

const res = await fetch('https://example.com/articles/foo', { headers })

Client with cache management

typescript
const fetch402 = create402Fetch({
  wallet,
  cacheTimeoutMs: 30 * 60 * 1000 // Cache for 30 minutes
})

// Make requests — auto-pays 402s with caching
const response = await fetch402('https://api.example.com/content')
const data = await response.json()

// Clear cache between sessions
fetch402.clearCache()

Key concepts

  • 402 Payment Required — HTTP status code for micropayments (BRC-121)
  • Server Headers (402 response) — x-bsv-sats, x-bsv-server for payment request
  • Client Headers (payment request) — BEEF, sender, nonce, time, vout for payment proof
  • Replay Protection — Timestamp freshness + wallet validation + an independent atomic transaction claim
  • Key Derivation — BRC-29 using server identity key + nonce + timestamp
  • Caching — Client caches paid content per URL to avoid re-payment
  • P2PKH Script — Payment always P2PKH: OP_DUP OP_HASH160 <hash> OP_EQUALVERIFY OP_CHECKSIG

When to use this

  • Building clients that interact with paid APIs
  • Creating applications that need to pay for content on-demand
  • Implementing automatic payment for API access
  • Building smart clients that handle 402 gracefully
  • Testing payment-gated services
  • Monetizing APIs with micropayments

When NOT to use this

  • Routes that already require BRC-31 HTTP auth — use @bsv/payment-express-middleware with @bsv/auth-express-middleware
  • Manual payment control needed — don't use auto-handler
  • Subscription-based access — use authentication instead
  • Large bulk payments — use dedicated payment processing

Spec conformance

  • BRC-121 — HTTP 402 Payment Required micropayments
  • BRC-29 — Key derivation (payment pubkey via nonce + timestamp)
  • BRC-42 — Public key derivation
  • BRC-100 — Wallet interface
  • P2PKH — Standard Bitcoin SV locking script
  • Atomic BEEF — The BRC-95 subject identifies the payment transaction whose output is priced and internalized

Common pitfalls

  1. Timestamp must be fresh — If x-bsv-time is >30 seconds old, payment is rejected
  2. Server identity key required — Server must send its identity public key in 402 response
  3. Transaction uniqueness — Replaying the same BEEF is rejected by the atomic replay store; a wallet's optional isMerge detail is defense in depth, not the public replay contract
  4. Cache timeout too long — If cached, same content served without payment check
  5. Atomic BEEF must be strict — Plain BEEF and trailing bytes are rejected; unrelated branches in a legacy atomic envelope cannot influence the price
  6. Wallet not available — Client-side create402Fetch requires working WalletClient
  7. calculatePrice undefined — If returns undefined, payment is skipped (content is free)
  8. Server clock skew — If clocks differ by >30 seconds, payment fails; use NTP
  9. Cluster replay state — The default replay store is process-local; inject one shared, durable, atomic PaymentReplayStore into every serving process

The middleware is silent by default and uses fixed bounded wallet descriptions. Supply its optional structured logger only when the deployment can protect transaction diagnostics. If wallet or replay-store processing fails after internalization starts, the middleware returns 503 without a new payment challenge so a client is not encouraged to spend twice. satoshisPaid reports the actual selected output value, including any overpayment.

Related packages

  • @bsv/payment-express-middleware — Server-side payment gating with Express
  • @bsv/auth-express-middleware — HTTP authentication (often paired with 402)
  • @bsv/sdk — WalletClient, PublicKey, Utils for payment construction

Reference

  • API reference (TypeDoc)
  • Source on GitHub
  • npm