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

@aios-medical/cashier

v1.4.0

Published

Unified subscription billing for Node.js and TypeScript. One API across payment providers, with typed errors and NestJS support.

Downloads

1,859

Readme

Cashier

npm License: MIT

Unified subscription billing for Node.js and TypeScript. One API across payment providers, with typed errors and NestJS and Express support. Stripe and Recurly are supported today.

Cashier puts payment providers behind one set of methods and types. Your code asks for a driver, calls driver.subscriptions.create(...), and gets back the same Subscription shape whichever provider is behind it. Each provider is a driver, so new providers can be added without changing your code. Provider errors are turned into a small set of typed errors, so you can handle a declined card or a missing customer the same way everywhere.

  • One interface for every provider, so you can switch providers or run several side by side.
  • Consistent results: amounts in minor units (cents), uppercase ISO 4217 currency codes, Date objects for timestamps.
  • Typed errors such as NotFoundError, PaymentFailedError and RateLimitError, with the original provider error kept as cause.
  • Works from CommonJS and ES modules, with TypeScript types included.
  • First-class NestJS (CashierModule.forRoot, injectable CashierService) and Express (req.cashier, error handler) packages.
  • Switch providers with one config value, or use other credentials at runtime.
  • No runtime dependencies besides the provider clients you install yourself.

Packages

| Package | Use it for | | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | | @aios-medical/cashier | The core library. Works in any Node.js or TypeScript project. | | @aios-medical/cashier-nestjs | NestJS module with forRoot, forRootAsync, CashierService and an exception filter. | | @aios-medical/cashier-express | Express middleware for req.cashier and an error handler. |

All packages are released together with the same version.

Providers

| Provider | Driver name | SDK (peer dependency) | | -------- | ------------------------- | ----------------------------------------------------------------- | | Stripe | CashierProvider.Stripe | stripe v13 | | Recurly | CashierProvider.Recurly | recurly v4.67 or later |

Install

npm install @aios-medical/cashier stripe recurly

Each driver uses its provider's official SDK, installed as a peer dependency. Install the SDKs of every provider in the table above, since Cashier currently loads all of them. Node.js 20 or later is required.

Quick start

import {
  CashierProvider,
  createCashier,
  PaymentFailedError,
} from '@aios-medical/cashier';

const cashier = createCashier({
  default: CashierProvider.Stripe,
  providers: {
    stripe: { apiKey: process.env.STRIPE_SECRET_KEY! },
    recurly: { apiKey: process.env.RECURLY_API_KEY! },
  },
});

const customer = await cashier.use().customers.create({
  email: '[email protected]',
  firstName: 'Jane',
  lastName: 'Doe',
});

try {
  const subscription = await cashier.use().subscriptions.create({
    customer: customer.id,
    price: 'price_123',
    currency: 'USD',
  });

  console.log(subscription.status, subscription.items[0]?.unitAmount);
} catch (error) {
  if (error instanceof PaymentFailedError) {
    console.log('Payment failed', error.declineCode);
  } else {
    throw error;
  }
}

use picks the provider for each call:

| Call | Driver | | ------------------------------------------------- | ---------------------------------------------------------------- | | cashier.use() | The default provider. Switch providers by changing the config. | | cashier.use(CashierProvider.Recurly) | A provider from providers. | | cashier.use(CashierProvider.Stripe, { apiKey }) | Other credentials, given at runtime. |

Drivers are created once and reused, so calling use on every request is cheap. See TypeScript.

Use with

TypeScript

The core package is all you need. Pass the Cashier to your classes so tests can replace it. See TypeScript.

NestJS

npm install @aios-medical/cashier-nestjs
@Module({
  imports: [
    CashierModule.forRootAsync({
      isGlobal: true,
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        default: CashierProvider.Stripe,
        providers: {
          stripe: { apiKey: config.getOrThrow('STRIPE_SECRET_KEY') },
        },
      }),
    }),
  ],
  providers: [{ provide: APP_FILTER, useClass: CashierExceptionFilter }],
})
export class AppModule {}

@Injectable()
export class BillingService {
  constructor(private readonly cashier: CashierService) {}

  getInvoice(invoiceId: string) {
    return this.cashier.use().invoices.get(invoiceId);
  }
}

See NestJS.

Express

npm install @aios-medical/cashier-express
app.use(cashierMiddleware(cashier));

app.get('/invoices/:id', async (req, res) => {
  res.json(await req.cashier.use().invoices.get(req.params.id));
});

app.use(cashierErrorHandler());

See Express.

Documentation

Every resource works the same way on each provider. The full reference, with parameters, provider behavior and returned objects, is in docs/.

| Resource | Methods | | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Customers | get, list, create, update | | Invoices | get, list, cursorPaginate, pay, void | | Payments | list, cursorPaginate, refund | | Products | get, list | | Prices | get, list | | Subscriptions | list, cursorPaginate, create, get, update, cancel |

Guides:

Errors

Every method rejects with a subclass of CashierError, such as NotFoundError, PaymentFailedError or RateLimitError. Each error has a code, the provider, the provider's HTTP status and error code, and the original error as cause. See Errors for the full list.

import { NotFoundError } from '@aios-medical/cashier';

try {
  await cashier.use().customers.get('cus_123');
} catch (error) {
  if (error instanceof NotFoundError) return null;
  throw error;
}

Contributing

See CONTRIBUTING.md. To report a security issue, see SECURITY.md.

License

MIT © AIOS