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

stripe-no-webhooks

v0.0.17

Published

Stripe integration without dealing with webhooks

Readme

stripe-no-webhooks

Why this library?

This is an opinionated library to help you implement payments with Stripe.

  1. Define plans in code which sync to your Stripe account
  2. No manual webhook setup - the library handles webhooks and syncs Stripe data to your DB
  3. Simple APIs for subscriptions, credits, wallet balances, top-ups, and usage-based billing
  4. Support for seat based billing, tax collection, plan upgrades and downgrades (including sane handling of credits)
  5. Optional callbacks (onSubscriptionCreated, etc.) for custom logic

Quick Start

This guide assumes you have a Next.js app and a PostgreSQL database. We recommend starting with a test mode Stripe API key so you can test your setup locally in your dev environment. Then, the guide will walk you through how to set up your app for production.

1. Install

npm install stripe-no-webhooks stripe
npx stripe-no-webhooks init

You'll be prompted for:

  • Stripe test key (for eg, sk_test_...) - get it from Stripe dashboard
  • Database URL – PostgreSQL connection string (for example: postgresql://postgres:password@localhost:5432/app_db)
  • Site URL - For eg, http://localhost:3000 for local dev

This will update your .env file with your credentials and create the following files:

  • billing.config.ts: Your config file with your plans
  • lib/billing.ts: Your core billing client instance
  • app/api/stripe/[...all]/route.ts: Your webhook handler and API routes

2. Set up database

npx stripe-no-webhooks migrate

This will create the stripe schema in your database with the necessary tables for syncing Stripe data and tracking credits + usage.

3. Define your plans

Edit billing.config.ts to define your plans for the test environment. Here's an example:

const billingConfig: BillingConfig = {
  test: {
    plans: [
      {
        name: "Free",
        price: [{ amount: 0, currency: "usd", interval: "month" }],
      },
      {
        name: "Pro",
        price: [
          { amount: 2000, currency: "usd", interval: "month" }, // $20/mo
          { amount: 20000, currency: "usd", interval: "year" }, // $200/yr
        ],
      },
    ],
  },
  production: {
    plans: [], // Add when going live
  },
};

Plans can also include credits, wallet, and usage-based billing:

{
  name: "Pro",
  price: [{ amount: 2000, currency: "usd", interval: "month" }],
  features: {
    api_calls: {
      credits: { allocation: 1000 },   // 1000 included/month
      pricePerCredit: 1,               // $0.01 per extra call (used for top-ups and usage billing)
      trackUsage: true,                // enable usage-based billing for overages
    },
  },
  wallet: {
    allocation: 500,                   // $5.00 prepaid balance
  },
}

See Credits, Wallet, and Usage Billing docs for details.

4. Sync to Stripe

npx stripe-no-webhooks sync

Creates products/prices in Stripe and updates your config with their IDs.

5. Update your billing client

Update lib/billing.ts to specify how to get the userId in the resolveUser function. For example, with Clerk:

import { Billing } from "stripe-no-webhooks";
import { auth } from "@clerk/nextjs/server"; // or your auth
import billingConfig from "../billing.config";

export const billing = new Billing({
  billingConfig,
  successUrl: process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000",
  cancelUrl: process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000",
  resolveUser: async () => {
    const { userId } = await auth();
    return userId ? { id: userId } : null;
  },
});

6. Test it

Start your Next.js app, then in another terminal, forward Stripe webhooks:

stripe listen --forward-to localhost:3000/api/stripe/webhook

Your setup is complete! Now let's use it.


Using the Library

Trigger a checkout

stripe-no-webhooks lets you generate a full pricing page with plan selection, monthly/yearly toggle, and checkout flow built-in. Or you can call checkout() directly:

import { checkout } from "stripe-no-webhooks/client";

<button onClick={() => checkout({ planName: "Pro", interval: "month" })}>
  Upgrade to Pro
</button>;

Test card: 4242 4242 4242 4242, any future MM/YY expiry, any CVC.

Check subscription status

import { billing } from "@/lib/billing";

const subscription = await billing.subscriptions.get({ userId });

if (subscription?.status === "active") {
  console.log("Plan:", subscription.plan?.name);
}

What happens behind the scenes

When a user completes checkout:

  1. Stripe sends a webhook to your app
  2. The library receives it and syncs the data to your database. If credits / wallet are enabled, it will also update the credits / wallet balances
  3. billing.subscriptions.get({ userId }) now returns the subscription based on the Stripe data that's synced to your database
  4. Credits / wallet are tracked automatically through a credit balance and a ledger of transactions via the library's internal APIs. These APIs are all idempotent and you don't have to worry about double counting or missing transactions

You can verify this by checking your database's stripe.subscriptions and stripe.credit_balances and stripe.credit_ledger.

Use credits, wallet, or usage billing

// Credits: consume included units
if (await billing.credits.hasCredits({ userId, key: "api_calls", amount: 1 })) {
  await billing.credits.consume({ userId, key: "api_calls", amount: 1 });
}

// Wallet: deduct from prepaid balance (in cents)
await billing.wallet.consume({
  userId,
  amount: 50,
  description: "AI generation",
});

// Usage: record for end-of-period billing
await billing.usage.record({ userId, key: "api_calls", amount: 1 });

See the full set of features in the Credits, Wallet, and Usage docs.

Open billing portal

Let users manage their subscription:

import { customerPortal } from "stripe-no-webhooks/client";

<button onClick={() => customerPortal()}>
  Manage Billing
</button>

React to events

export const billing = new Billing({
  billingConfig,
  callbacks: {
    onSubscriptionCreated: async (subscription) => {
      // Send welcome email
    },
    onSubscriptionCancelled: async (subscription) => {
      // Clean up resources
    },
    // List of full callbacks in docs/reference.md
  },
});

Generate a Pricing Page

npx stripe-no-webhooks generate pricing-page

This creates a fully customizable pricing page component at components/PricingPage.tsx:

// in your pricing page, for eg /pricing
import { PricingPage } from "@/components/PricingPage";

export default function Pricing() {
  return <PricingPage />;
}

Automatically handles: plan fetching, current subscription detection, monthly/yearly toggle, checkout flow, redirect handling, error handling, and more.


Going to Production

  1. Add plans to the production section of billing.config.ts
  2. Run npx stripe-no-webhooks sync and choose "Set up for production". This will:
  • Sync your plans to Stripe live mode
  • Create a webhook endpoint for your app in Stripe
  • Display the webhook URL and secret in the CLI output
  1. Add the webhook secret to your production environment (for eg, Vercel environment variables):
    STRIPE_WEBHOOK_SECRET=whsec_...
    STRIPE_SECRET_KEY=sk_live_... # IMPORTANT: Your live Stripe secret key
    DATABASE_URL=postgresql://[username]:[password]@[production-db-url]:5432/[production-db-name]
    NEXT_PUBLIC_APP_URL=https://your-production-app.com

More Features

| Feature | Use Case | | ---------------------------------------------- | ---------------------------------------------------- | | Credits | "1000 API calls/month included" - consumable units | | Wallet | "$5/month for AI usage" - prepaid spending balance | | Top-ups | Let users buy more credits on demand | | Usage Billing | "Pay $0.10 per API call" - post-paid metered billing | | Team Billing | Bill organizations, per-seat pricing | | Tax Collection | Automatic VAT/GST calculation and ID collection | | Payment Failures | Handle declined cards, retry logic |


CLI Commands

| Command | Description | | ----------------------- | ------------------------------ | | init | Set up config files and .env | | migrate | Create database tables | | sync | Sync plans to Stripe | | generate pricing-page | Generate pricing component | | backfill | Import existing Stripe data |


API Reference

See docs/reference.md for the complete API.

If you are an LLM, full documentation is at https://github.com/pretzelai/stripe-no-webhooks/blob/main/docs/llms.txt