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

@wtfalch/billing

v0.1.1

Published

Products, plans and subscriptions: a flat monthly plan per organisation plus metered usage rated from @wtfalch/ledger, converted to @wtfalch/payments' minor units at charge time. Net of MVA.

Readme

@wtfalch/billing

Products, plans and subscriptions: a flat monthly plan per organisation plus metered usage rated from @wtfalch/ledger, converted to @wtfalch/payments' minor units at charge time. Amounts are net of MVA (Norwegian VAT) -- @wtfalch/invoicing adds MVA, this package never computes it.

The library shape: one published package, no server, no database this package owns a credential to. The host supplies a Database (the same one wired into @wtfalch/ledger, since rating a period reads ledger's own tables) and calls this package's plain functions.

Install

pnpm add @wtfalch/billing @wtfalch/ledger @wtfalch/payments

@wtfalch/authz is an optional peer, scaffolded per the estate's usual convention -- see src/catalogue.ts.

Usage

import { migrate as migrateBilling } from '@wtfalch/billing';
import { migrate as migrateLedger } from '@wtfalch/ledger';

// once, at startup, against the host's own Database:
await migrateLedger(db);
await migrateBilling(db);
import { createPlan, createProduct, setPlanMeter } from '@wtfalch/billing';

const product = await createProduct(db, {
  sellerOrganisationId: estateOrgId,
  key: 'estate',
  name: 'Estate plan',
});

const plan = await createPlan(db, {
  productId: product.id,
  key: 'pro',
  name: 'Pro',
  currency: 'NOK',
  priceMicros: '299000000', // 299.00 NOK/month, net of MVA
});

// AI usage is billed at cost, plus a 10% markup, above a 50 NOK allowance:
await setPlanMeter(db, {
  planId: plan.id,
  meter: 'ai',
  includedMicros: '50000000',
  markupBp: 1000,
});
import { createSubscription, createCharge, chargeToPaymentAmount } from '@wtfalch/billing';

const subscription = await createSubscription(db, {
  planId: plan.id,
  buyerOrganisationId: customerOrgId,
  trialDays: 14,
});

// after the trial ends and the subscription is active (see below):
const charge = await createCharge(db, subscription.id, subscription.currentPeriodStart);
const amount = chargeToPaymentAmount(charge); // { currency: 'NOK', value: 29900 }
// hand `amount` to @wtfalch/payments' createPayment / chargeRecurringAgreement
import { activateSubscription, markPastDue, recordDunningAttempt } from '@wtfalch/billing';

// a payment provider's webhook reports the charge failed:
await recordDunningAttempt(db, { subscriptionId: subscription.id, succeeded: false, nextRetryAt: tomorrow });
await markPastDue(db, subscription.id);

// ...and later, a retry succeeds:
await recordDunningAttempt(db, { subscriptionId: subscription.id, succeeded: true });
await activateSubscription(db, subscription.id);

Public surface

  • Products and plans: createProduct, getProduct, listProducts; createPlan, getPlan, setPlanMeter, listPlanMeters.
  • Subscriptions: createSubscription, getSubscription, and the state machine -- activateSubscription, markPastDue, cancelSubscription (trial → active → past_due ⇄ active, any non-cancelled status → cancelled, which is terminal).
  • Dunning: recordDunningAttempt, dueDunningRetries -- a log and a query, not a scheduler. A @wtfalch/jobs handler sweeps dueDunningRetries, attempts a charge, and calls recordDunningAttempt plus activateSubscription/markPastDue itself; this package runs no schedule of its own.
  • Rating: ratePeriod (read-only) and createCharge (rates and persists, idempotent per subscription/period).
  • Money: minorUnitDecimals, toMinorUnits -- the one place ledger's integer micros become payments' integer minor units, rounding once, half up, per charge line.
  • Migrations: migrate.
  • Authorization: catalogue, resourceModule, checkBillingManage, checkBillingRead (billing:configure, billing:read).

What this package does not do

  • Rate usage itself. files, ai and valet already turn their own usage into money before it reaches a ledger row (files_rates/metering.ts, offerings.ts, priceBook); this package reads that already-priced usage from @wtfalch/ledger and only adds an optional allowance/markup "on top" per plan. Duplicating those rate tables here would be a fourth copy of the same pricing.
  • Compute MVA. @wtfalch/invoicing does.
  • Take the payment. @wtfalch/payments does -- this package hands it a Money amount in minor units via chargeToPaymentAmount.
  • Run a schedule. dueDunningRetries is a query a @wtfalch/jobs handler calls; nothing here ticks on its own.

Status

v1 built: products, plans (with optional per-meter allowance/markup), subscriptions (trial/active/past_due/cancelled, with dunning logging and a due-retries query), period rating against @wtfalch/ledger usage, and the micros→minor-units conversion. @wtfalch/ledger and @wtfalch/payments are both merged on main in their own repos but not yet published to npm -- see docs/adr/0012. Not published to npm itself -- publishing needs the user's npm 2FA, and in any case waits on both of those.