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

d402

v0.4.2

Published

HTTP 402 payment agreement SDK backed by on-chain state.

Readme

d402

d402 adds verifiable on-chain payments to HTTP. A server returns a 402 challenge, the client creates the matching dPayment, and the server authorizes the retried request from its proof.

Install

npm install d402 @d402/ethers ethers

1. Protect a complete route

Use payable() when the protected handler can be expressed as a Fetch-style route:

import { JsonRpcProvider, Wallet } from "ethers";
import { createEthersAdapter } from "@d402/ethers";
import { payable } from "d402/server";

const provider = new JsonRpcProvider(process.env.RPC_URL);
// Any ethers Signer works here, including a KMS or custody-backed signer.
// Signer is only needed if the server performs an on-chain action during the request (for example, Once consumption or refunds).
const payee = new Wallet(process.env.PAYEE_PRIVATE_KEY, provider);
const adapter = createEthersAdapter({ provider, signer: payee });
const terms = {
  chainId: 100,
  payeeAddress: payee.address,
  tokenAddress: null,
  netAmount: "1000000000000000",
  agreement: { id: "monthly-report:v1" },
  expiresAtUnixSec: Math.floor(Date.now() / 1000) + 300,
};

export const GET = payable({
  adapter,
  payment: { settlementWindow: 3600 },
  terms,
  handler: (_request, payment) =>
    Response.json({
      report: "protected data",
      paymentId: payment.payment.paymentId,
    }),
});

The request URL is the payment resource by default. terms may also be an async function when price or agreement data depends on the request.

2. Authorize inside a controller

Use PaymentAuthorizer when your framework or application owns the controller:

import { PaymentAuthorizer } from "d402/server";

const reportPayment = new PaymentAuthorizer({
  adapter,
  payment: { settlementWindow: 3600 },
  terms,
});

export async function getReport(request: Request): Promise<Response> {
  const authorization = await reportPayment.authorize(request);

  if (authorization.response !== undefined) {
    return authorization.response;
  }

  const report = await loadReport();

  return Response.json({
    report,
    paymentId: authorization.context.payment.paymentId,
  });
}

authorize() returns a protocol response when the request needs a challenge, retry, or rejection. It returns context only after authorization succeeds. Express middleware, Nest guards, and other framework adapters can translate their native request into a Fetch Request and use this same API.

3. Make each payment single-use

Routes are reusable by default. Add Once when one payment should authorize at most one protected operation:

import { Once, payable, paymentActions } from "d402/server";

const actions = paymentActions({
  adapter,
  payment: {},
});

const download = payable({
  adapter,
  payment: { settlementWindow: 3600 },
  terms,
  consumer: Once(actions),
  handler: async () =>
    Response.json({ downloadUrl: await createDownloadUrl() }),
});

Once is an on-chain, at-most-once claim. If work must survive a crash or lost HTTP response, store the result under context.payment.paymentId.

Pay for an order

Use the default server identity when payment terms represent a stable order or invoice. Put the order ID in the terms so retries reconstruct the same payment:

const orderRoute = payable({
  adapter,
  payment: {},
  terms: (request) => {
    const orderId = new URL(request.url).pathname.split("/").at(-1);

    return {
      chainId: 100,
      payeeAddress: payee.address,
      tokenAddress: null,
      netAmount: "1000000000000000",
      agreement: { id: `order:${orderId}:v1` },
      expiresAtUnixSec: Math.floor(Date.now() / 1000) + 300,
    };
  },
  handler,
});

The same payer, order terms, and server identity produce the same payment identity. This is useful for orders, invoices, and reusable entitlements.

Choose independent payment identity

Use client identity for independent per-request payments. This is closer to an x402-style flow, where each request or access attempt should create a fresh payment instead of reusing an order identity:

import { Once, payable, paymentActions } from "d402/server";

const actions = paymentActions({
  adapter,
  payment: {},
});

const independentPaymentRoute = payable({
  adapter,
  payment: { identifier: "client" },
  terms,
  consumer: Once(actions),
  handler,
});

Client identity is separate from single-use access. Use Once when a payment may authorize only one operation, and use client identity when each payment attempt should have an independent payment identity. They can be used together when every new payment should authorize at most one operation.

Pay a protected route

For a server, agent, or other unattended client, connect an ethers Wallet to the provider:

import { JsonRpcProvider, Wallet } from "ethers";
import { createD402Client } from "d402/client";

const provider = new JsonRpcProvider(process.env.RPC_URL);
const signer = new Wallet(process.env.PAYER_PRIVATE_KEY, provider);
const client = await createD402Client({ provider, signer });

const response = await client.fetch(
  "https://api.example.com/reports/monthly",
);

In a browser, use the signer exposed by MetaMask or another EIP-1193 wallet:

import { BrowserProvider } from "ethers";
import { createD402Client } from "d402/client";

const provider = new BrowserProvider(window.ethereum);
await provider.send("eth_requestAccounts", []);
const signer = await provider.getSigner();
const client = await createD402Client({ provider, signer });

const response = await client.fetch(
  "https://api.example.com/reports/monthly",
);

The client signer is always the payer. Browser wallets prompt the user when d402 sends a payment transaction; ERC-20 payments may also require a separate token approval transaction.

Use client.d402Fetch() when the completed payment attempt must be persisted for application recovery or later lifecycle decisions.

Choose a payment flow

The most important integration guide is Payment flows. It shows when to use reusable access, single-use access, stable orders, jobs, credits, and deposits.

Documentation

License

Apache-2.0