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

@speeddev/l402-express

v0.1.1

Published

Express middleware for L402 Lightning Network payments — issues Speed payment challenges, verifies macaroon credentials, and gates API access to paid requests only

Readme

@speeddev/l402-express

npm version License: MIT Node.js: >=18

Express middleware that enforces L402 payment-required flows using Speed Lightning invoices and macaroon credentials.


Contents


How it works

L402 is an HTTP-native payment protocol built on Lightning Network. The flow has three steps:

  1. Challenge — a client makes a request to a protected endpoint without payment credentials. The middleware responds with 402 Payment Required, a Lightning invoice (created using your Speed account's API Key), and a signed macaroon.
  2. Payment — the client pays the Lightning invoice and receives a preimage (proof of payment).
  3. Access — the client retries the request with an Authorization: L402 <macaroon>:<preimage> header. The middleware verifies the macaroon signature, the caveats (method, path, amount, currency, expiry), and the preimage against the payment hash. On success the request is forwarded to your route handler.

Note: Responses to verified requests are cached for the duration of the macaroon's TTL (default 10 minutes), so replaying a valid credential returns the cached response instantly without hitting your handler again.


Prerequisites

Getting a Speed API key

  1. Log in to the Speed Merchant.
  2. Navigate to Developers → API keys → Standard keys.
  3. Create a Restricted Key : Speed recommends using a restricted key (rk_live_...) for the speedApiKey option. While creating the restricted key, select Core → Payments as the module and grant Write permission.

Standard secret keys (sk_live_...) will also work, but they provide broader account privileges (including Send functionality).  Hence for better security and least-privilege access, we recommend using a restricted key.


Installation

npm install @speeddev/l402-express

express is a peer dependency and must be installed separately if you have not done so already:

npm install express

Quick start

import express from 'express';
import l402Middleware from '@speeddev/l402-express';

const app = express();

app.use(
  l402Middleware({
    speedApiKey: process.env.SPEED_KEY,
    macaroonSecret: process.env.SPEED_MACAROON_SECRET,
    configs: [
      {
        method: 'GET',
        path: '/api/report',
        amount: 1,
        currency: 'USD',
        targetCurrency: 'SATS',
      },
    ],
  })
);

app.get('/api/report', (req, res) => {
  res.json({ data: 'paid content here' });
});

app.listen(3000);

Any route not listed in configs passes through freely without a payment check.


Configuration reference

Middleware options

| Option | Type | Required | Description | |---|---|---|---| | speedApiKey | string | Yes | Your Speed API key (rk_live_… or sk_live_…). | | macaroonSecret | string | Yes | 32-byte hex-encoded secret used to sign and verify macaroons. Generate one with npx l402-generate-secret. | | caveatTtlMs | number | No | Macaroon TTL in milliseconds. Defaults to 10 minutes when omitted. Must be a positive number if provided. | | configs | RouteConfig[] | Yes | Array of route-level payment rules. |

RouteConfig

| Field | Type | Required | Description | |---|---|---|---| | method | string | Yes | HTTP method to match (e.g. 'GET', 'POST'). Case-insensitive — normalized to uppercase automatically. | | path | string | Yes | path-to-regexp pattern (e.g. '/api/resource/:id'). | | amount | number | Yes | Payment amount you intend to collect. | | currency | string | Yes | ISO 4217 currency code for the payment amount (e.g. 'USD', 'EUR', 'SATS'). | | targetCurrency | string | No | Cryptocurrency to settle in: 'SATS', 'USDT', or 'USDC'. Defaults to 'SATS' when omitted. |

Example — charge $2.50 USD, settle in SATS:

{
  method: 'POST',
  path: '/api/generate',
  amount: 2.5,
  currency: 'USD',
  targetCurrency: 'SATS',
}

Example — charge 1000 sats directly:

{
  method: 'GET',
  path: '/api/data',
  amount: 1000,
  currency: 'SATS',
}

Generating the macaroon secret

The macaroon secret is a private 32-byte key used to sign credentials. It must be kept secret — anyone who obtains it can forge valid macaroons.

Generate one with the CLI tool bundled in this package:

npx l402-generate-secret

This prints a 64-character hex string. Store it as an environment variable and never commit it to source control.

b3f1a2c4e5d6...  ← copy this into SPEED_MACAROON_SECRET

Rotate this secret if you suspect it has been compromised. All previously issued macaroons will immediately become invalid.


API key security

  • Never embed secret keys in client-side code, public repositories, or anywhere outside a secure server environment.
  • Consider restricted API keys to limit the scope of what each key can do.
  • Store keys in environment variables or a secrets manager (e.g. AWS Secrets Manager, HashiCorp Vault).
  • If a key is compromised, rotate it immediately from the Speed dashboard.

Environment variables

The middleware does not read environment variables directly. Pass your secrets explicitly via the options object:

l402Middleware({
  speedApiKey: process.env.SPEED_KEY,
  macaroonSecret: process.env.SPEED_MACAROON_SECRET,
  configs: [...],
})

Recommended .env layout (use dotenv or your platform's secret injection):

SPEED_KEY=rk_live_...
SPEED_MACAROON_SECRET=<64-char hex from npx l402-generate-secret>

Error responses

| Status | Condition | Body | |---|---|---| | 402 | No Authorization header — payment required. | {} with WWW-Authenticate header containing the macaroon and Lightning invoice. | | 400 | Authorization header present but malformed or macaroon verification failed. | { "message": "Malformed 'authorization' header" } | | 401 | Preimage does not match the payment hash in the macaroon. | { "message": "Invalid payment preimage" } | | 409 | A request with the same macaroon is already being processed. | { "message": "Payment is already being processed" } | | 500 | Speed API call failed due to any reason. | { "message": "Internal server error" } |

WWW-Authenticate header format

L402 macaroon="<base64-encoded macaroon>", invoice="<BOLT11 invoice>"

The client pays the invoice and uses the returned preimage together with the macaroon to form the Authorization header:

Authorization: L402 <macaroon>:<preimage>

TypeScript

The package ships with a bundled declaration file. No @types package is needed. Named types RouteConfig and L402MiddlewareOptions are available as named exports.


License

MIT