npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bsv/payment-express-middleware

v2.1.8

Published

BSV Blockchain service monetization express middleware

Readme

@bsv/payment-express-middleware

Express middleware for the legacy authenticated x-bsv-payment JSON flow. It runs after @bsv/auth-express-middleware, issues an HTTP 402 challenge, validates an Atomic BEEF transaction, atomically rejects reused transaction IDs, internalizes output zero, and exposes a verified payment receipt.

This protocol is distinct from the newer BRC-121 implementation in @bsv/402-pay. Choose one protocol deliberately; their headers and client contracts are not interchangeable.

Version 2.1.8 removes middleware payment-header size ceilings. A header already received from the client is parsed and its payment validated regardless of size. HTTP server, CDN, proxy and WAF configuration own transport budgets; configure and validate the full route for supported payment proofs. The deprecated maxPaymentHeaderBytes option remains accepted for source compatibility but is ignored, including when an older application still supplies a value. Move any intended transport policy to the HTTP server or edge and remove the option.

Atomic BEEF validation, canonical base64, derivation verification, payment pricing, wallet acceptance and atomic replay protection still apply. Invalid payment contents are rejected. SDK 2.8.5 separately raises client header capacity; that does not constrain what this middleware accepts from other compatible clients. No BRC100 call, wire or wallet-data migration is required. Source publication is a separate protected release step.

Requirements

  • Node.js 22 or newer
  • Express 4.18 or newer, including Express 5
  • @bsv/auth-express-middleware earlier in the middleware chain
  • A BRC-100 wallet implementing internalizeAction

The package ships native ESM and CommonJS entry points with declarations for both module systems.

Install

npm install @bsv/payment-express-middleware @bsv/auth-express-middleware @bsv/sdk express

Express is a peer dependency, so the middleware uses the application's single Express runtime and type graph. This prevents duplicate Express installations from making PaymentRequest or the returned middleware incompatible with the application's route types.

Basic use

import express from 'express'
import { createAuthMiddleware } from '@bsv/auth-express-middleware'
import { createPaymentMiddleware, type PaymentRequest } from '@bsv/payment-express-middleware'

const app = express()

app.use(express.json())
app.use(createAuthMiddleware({ wallet }))
app.use(
  createPaymentMiddleware({
    wallet,
    calculateRequestPrice(req) {
      if (req.path === '/free') return 0
      if (req.path === '/premium') return 500
      return 100
    },
    replayStore
  })
)

app.get('/premium', (req: PaymentRequest, res) => {
  res.json({
    accepted: req.payment?.accepted,
    satoshisPaid: req.payment?.satoshisPaid,
    txid: req.payment?.txid
  })
})

Prices must be 0 or a positive safe integer. Zero-cost requests continue with an accepted zero-value receipt. Invalid or failed pricing returns a stable 500 response and never authorizes the request.

Flow

  1. Auth middleware verifies the peer and supplies a compressed req.auth.identityKey.

  2. calculateRequestPrice returns the required satoshis.

  3. If no x-bsv-payment header is present, the middleware returns 402 with:

    • x-bsv-payment-version: 1.0
    • x-bsv-payment-satoshis-required
    • x-bsv-payment-derivation-prefix
  4. The client retries with one JSON header:

    {
      "derivationPrefix": "<canonical-base64>",
      "derivationSuffix": "<canonical-base64>",
      "transaction": "<base64-atomic-beef>"
    }
  5. The middleware parses the received header, verifies the derivation prefix, parses the Atomic BEEF transaction, reduces legacy overinclusive envelopes to the declared subject and its dependency closure, and requires output zero to cover the current price.

  6. The wallet must validate and newly accept the remittance with { accepted: true } from internalizeAction. Merge/replay-like or malformed results do not authorize the route.

  7. Only after wallet validation does the middleware atomically claim the transaction ID. This prevents invalid derivation material paired with a public transaction from poisoning the replay store.

  8. next() runs with req.payment, and x-bsv-payment-satoshis-paid reports the actual output value.

Wallet verdicts must expose accepted and optional isMerge as own data properties; inherited or accessor-backed authority is rejected without invoking accessors. Malformed, duplicate, underfunded, rejected, or ambiguous payments never call next.

Options

const payment = createPaymentMiddleware({
  wallet,
  calculateRequestPrice,
  replayStore,
  logger
})
  • wallet is required and must provide internalizeAction.
  • calculateRequestPrice may be synchronous or asynchronous and defaults to 100 satoshis.
  • replayStore must implement an atomic claim(transactionId): boolean | Promise<boolean>. It returns false if the transaction has already been used.
  • maxPaymentHeaderBytes is deprecated and ignored; header budgets belong to the HTTP server or edge.
  • logger may provide error and warn methods. Internal failures are sent to it as structured context but are never exposed in HTTP responses.

Invalid option types fail during startup.

Replay storage

InMemoryPaymentReplayStore is the safe single-process default. It:

  • atomically claims each transaction ID once within one process;
  • records only transactions the wallet reported as newly accepted;
  • refuses new claims when its fixed capacity is reached rather than evicting an older replay marker; and
  • loses all claims when the process restarts.

Its default capacity is 100,000 claims. It is appropriate for tests and bounded single-process services, not a horizontally scaled or durable deployment.

Production services should inject a shared durable store backed by a database or cache primitive with atomic insert-if-absent semantics:

const replayStore = {
  async claim(transactionId: string) {
    return await database.insertPaymentClaimIfAbsent(transactionId)
  }
}

Do not implement claim as separate read and write operations. Keep replay claims for at least as long as a transaction could otherwise be accepted again. A derivation nonce proves that the server created the prefix; it is not an expiring, single-use replay database.

Payment receipt

After authorization:

interface PaymentReceipt {
  satoshisPaid: number
  accepted: true
  tx: string
  txid: string
}

satoshisPaid is the actual value of output zero, which may be greater than the required price. For free requests it is zero and tx/txid are empty.

Error behavior

| Status | Code | Meaning | | ------ | ------------------------------- | ------------------------------------------------------------------------ | | 400 | ERR_MALFORMED_PAYMENT | The header is duplicated, oversized, invalid JSON, or has invalid fields | | 400 | ERR_INVALID_DERIVATION_PREFIX | The server did not create the supplied prefix | | 400 | ERR_INVALID_PAYMENT | Atomic BEEF is invalid or output zero is underfunded | | 400 | ERR_PAYMENT_FAILED | The wallet could not accept the payment | | 402 | ERR_PAYMENT_REQUIRED | A payment challenge was issued | | 409 | ERR_PAYMENT_REPLAYED | The transaction was already claimed or was not newly accepted | | 500 | ERR_SERVER_MISCONFIGURED | Auth middleware did not provide a valid identity | | 500 | ERR_PAYMENT_INTERNAL | Pricing failed or returned an invalid value | | 503 | ERR_PAYMENT_UNAVAILABLE | Challenge creation or replay storage is unavailable |

Public errors deliberately omit wallet, replay-store, and pricing exception messages.

Public services and browser access

This middleware does not impose CORS or CSP. Public payment services can remain cross-origin by default while operators optionally configure an allowlist at the application or edge layer. Browser clients need the x-bsv-payment-* response headers exposed through CORS. Never pair a wildcard origin with credentialed CORS.

Security notes

  • Use HTTPS; payment and identity headers are not a confidentiality layer.
  • Run authentication first and authorization/payment routes after both middleware functions.
  • Use a durable atomic replay store for multiple processes or replicas.
  • Wallet errors and rejected remittances are never inserted into the replay store; investigate an unavailable replay store promptly because the wallet may already have accepted the transaction before that independent gate.
  • Monitor 409 and 503 rates and replay-store capacity.
  • Treat pricing as security-sensitive, deterministic request policy.
  • The wallet remains responsible for validating and safely internalizing the supplied BRC-29 remittance.
  • Apply normal request/header limits and rate limiting at the service edge.
  • Logger failures are contained and cannot interrupt payment authorization or change the HTTP result.

API

Runtime exports:

  • createPaymentMiddleware
  • InMemoryPaymentReplayStore

Type exports:

  • BSVPayment
  • PaymentLogger
  • PaymentMiddlewareOptions
  • PaymentReceipt
  • PaymentReplayStore
  • PaymentRequest

See API.md for generated signatures.

Development

pnpm typecheck
pnpm lint
pnpm format:check
pnpm test:coverage
pnpm pack:check

The test suite is deterministic and does not call public APIs. pack:check builds and validates the exact npm tarball in ESM and CommonJS consumer probes. Tests do not rebuild the package as a side effect.

License

Current TS Stack changes are licensed under the Open BSV License Version 6; see LICENSE.txt. This package also retains pre-uniformization code under the Open BSV License Version 4. Redistributors must preserve THIRD_PARTY_NOTICES.md and the applicable text in LICENSES/.