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

@billmyagent/x402-express

v1.1.0

Published

Charge for an Express API with x402, settled and reconciled through BillMyAgent

Readme

@billmyagent/x402-express

Charge for an Express API with x402, settled and reconciled through BillMyAgent.

npm install @billmyagent/x402-express express
import express from 'express';
import { paymentMiddleware } from '@billmyagent/x402-express';

const app = express();

app.use(
  await paymentMiddleware({
    apiKey: process.env.BILLMYAGENT_API_KEY,
    network: 'base',
    routes: {
      'GET /premium': { price: '$0.01', config: { description: 'Premium data' } },
    },
  })
);

app.get('/premium', (_req, res) => res.json({ ok: true }));
app.listen(4021);

GET /premium now answers 402 Payment Required until a caller presents a valid x402 payment. Any x402 client pays it automatically — @billmyagent/payments-core, x402-axios, x402-fetch, or an agent framework that speaks the protocol.

What this adds over x402-express

This is a thin wrapper over Coinbase's official middleware. It does two things that one cannot.

It routes the payment through BillMyAgent's facilitator, authenticated with your API key. Every payment your API takes is then recorded, checked against the chain by the reconciler, and visible in your dashboard. Without it, payments happen entirely between the buyer and Coinbase's facilitator and you have no record beyond your own logs.

It resolves your payout address from your account instead of asking you to paste it into your server config. payTo is an address that decides where money goes. A typo in an environment variable sends a real payment to a stranger, and it looks like a successful transfer from every side. Reading it from the account you registered — the same address the facilitator checks every payment against before broadcasting — means the value can only be wrong in one place, where you can see and fix it.

Non-custodial

The buyer's signed EIP-3009 authorization transfers USDC to your address directly. BillMyAgent never holds the funds and never appears in the transaction. The platform fee is a separate accrual against a settlement the reconciler has independently confirmed on-chain — it is not deducted from the transfer.

Timing: your handler runs before settlement

The x402 middleware verifies the payment, runs your route handler, and settles afterwards. So a settlement that fails means you already served that response for free.

This is how the protocol works and is identical with every facilitator, ours and Coinbase's alike. For a cheap endpoint it is noise. For an expensive one, do the cheap part in the handler and the expensive part after you have seen the X-PAYMENT-RESPONSE header.

Sandbox keys

A sk_test_ key settles on base-sepolia only. The gateway does not offer it a live network at all, so a sandbox key configured for base fails at startup here — when you deploy — rather than when a buyer tries to pay.

Testnet USDC is free from faucet.circle.com. Use a sk_live_ key to take real payments.

Networks

base and base-sepolia, settled in USDC. There is no Ethereum L1 settlement. Register a payout address for the network you want in the dashboard under Settings → Payouts before your first paid request.

Use base-sepolia to develop against free testnet USDC.

API

paymentMiddleware(options)

Returns a Promise<RequestHandler>. Await it once at startup — the payout address is resolved then, so a misconfigured account fails at deploy rather than when a buyer arrives.

| Option | Default | Meaning | | --- | --- | --- | | apiKey | required | Your BillMyAgent API key | | routes | required | { 'GET /path': { price: '$0.01' } }, or { '/path': '$0.01' } | | network | 'base' | Default chain for routes that do not name one | | baseUrl | https://api.billmyagent.ai | Gateway to talk to | | payTo | resolved | Override the payout address and skip the lookup | | paywall | – | Passed through to the official browser paywall page |

facilitator({ apiKey, baseUrl })

The FacilitatorConfig on its own, if you would rather call the official paymentMiddleware yourself and keep your own payTo.

resolvePayTo({ apiKey, baseUrl, network })

The payout address registered for a network. Throws if none is registered — there is no safe fallback for "where should this money go".

Errors you might see

| Message | Cause | | --- | --- | | this is a sandbox API key (sk_test_), so it cannot settle on <network> | A sk_test_ key is offered testnet only. Use network: 'base-sepolia', or a sk_live_ key for real funds | | your account has no payout address registered for <network> | Set one in the dashboard, then restart | | could not reach the facilitator (401 …) | The API key is wrong, revoked, or its owner's email is unverified | | A buyer sees invalid_exact_evm_payload_recipient_mismatch | The payTo in the request is not your registered address. If you passed payTo yourself, that is the mismatch | | A buyer sees duplicate_settlement | That authorization was already settled. The client should sign a fresh one |

License

MIT