Show HN: Stripe-no-webhooks – Sync your Stripe data to your Postgres DB (github.com)

66 points by prasoonds ↗ HN
Hey HN, stripe-no-webhooks is an open-source library that syncs your Stripe payments data to your own Postgres database: https://github.com/pretzelai/stripe-no-webhooks.

Here's a demo video: https://youtu.be/cyEgW7wElcs

Why is this useful? (1) You don't have to figure out which webhooks you need or write listeners for each one. The library handles all of that. This follows the approach of libraries like dj-stripe in the Django world (https://dj-stripe.dev/). (2) Stripe's API has a 100 rpm rate limit. If you're checking subscription status frequently or building internal tools, you'll hit it. Querying your own Postgres doesn't have this problem. (3) You can give an AI agent read access to the stripe.* schema to debug payment issues—failed charges, refunds, whatever—without handing over Stripe dashboard access. (4) You can join Stripe data with your own tables for custom analytics, LTV calculations, etc.

It creates a webhook endpoint in your Stripe account to forward webhooks to your backend where a webhook listener stores all the data into a new stripe.* schema. You define your plans in TypeScript, run a sync command, and the library takes care of creating Stripe products and prices, handling webhooks, and keeping your database in sync. We also let you backfill your Stripe data for existing accounts.

It supports pre-paid usage credits, account wallets and usage-based billing. It also lets you generate a pricing table component that you can customize. You can access the user information using the simple API the library provides:

  billing.subscriptions.get({ userId });
  billing.credits.consume({ userId, key: "api_calls", amount: 1 });
  billing.usage.record({ userId, key: "ai_model_tokens_input", amount: 4726 });
Effectively, you don't have to deal with either the Stripe dashboard or the Stripe API/SDK any more if you don't want to. The library gives you a nice abstraction on top of Stripe that should cover ~most subscription payment use-cases.

Let's see how it works with a quick example. Say you have a billing plan like Cursor (the IDE) used to have: $20/mo, you get 500 API completions + 2000 tab completions, you can buy additional API credits, and any additional usage is billed as overage.

You define your plan in TypeScript:

  {
    name: "Pro",
    description: "Cursor Pro plan",
    price: [{ amount: 2000, currency: "usd", interval: "month" }],
    features: {
      api_completion: {
        pricePerCredit: 1,              // 1 cent per unit
        trackUsage: true,               // Enable usage billing
        credits: { allocation: 500 },
        displayName: "API Completions",
      },
      tab_completion: {
        credits: { allocation: 2000 },
        displayName: "Tab Completions",
      },
    },
  }
Then on the CLI, you run the `init` command which creates the DB tables + some API handlers. Run `sync` to sync the plans to your Stripe account and create a webhook endpoint. When a subscription is created, the library automatically grants the 500 API completion credits and 2000 tab completion credits to the user. Renewals and up/downgrades are handled sanely.

Consume code would look like this:

  await billing.credits.consume({
    userId: user.id,
    key: "api_completion",
    amount: 1,
  });
And if they want to allow manual top-ups by the user:

  await billing.credits.topUp({
    userId: user.id,
    key: "api_completion",
    amount: 500,     // buy 500 credits, charges $5.00
  });
Similarly, we have APIs...

17 comments

[ 3.1 ms ] story [ 46.1 ms ] thread
This is really cool. How do you handle changes of pricing with something like this?
Haha "Stripe-no-webhooks library"

Library based on processing ... wait for it ... webhooks".

why call it no webhook when it's wait for it a webhook?
It is indeed painful to figure out all the Stripe events one needs to handle. However, for me it’s worth it to do it once, and then be able to handle the interactions between my app and Stripe directly, instead of adding more layers.
If used, will you now have to be PCI-compliant?
> You don't have to figure out which webhooks you need or write listeners for each one. The library handles all of that. This follows the approach of libraries like dj-stripe in the Django world (https://dj-stripe.dev/).

I don’t figure out these types of things anymore. I just make sure they work in dev, staging and production.

this doesn't work. Most of people follow the workflow you described and call it a day. The problem comes later when, for example, you realise stripe gives a grace period when payment for a subscription fails but the subscription still shows as active in your backend (by this time bad actors have been abusing your software for months).

With stripe-no-webhooks the default behaviour is that subscriptions get cancelled when payment fails and you can deal with your internal logic by putting it inside onSubscriptionCancelled hook.

There are many of small quirks that can be extremely costly (for example if you offer an AI product) and we take care of this for you

Thank you for sharing. I've been using the Better-Auth's Stripe integration but it has some quirks.

Which Stripe version does your lib support? Clover?

other contributor of stripe-no-webhooks here. Yes we support Clover but should be compatible with most versions, if you have any problem open a github issue and we'll fix it ASAP
OK, so how does it differ from Supabase one? https://github.com/supabase/stripe-sync-engine
stripe-no-webhooks integrates automatically with nextjs via cli & gives you useful abstractions on top of postgres like await billing.credits.consume({ userId, key: "api_calls", amount: 1 });
Something strange is afoot with this post -- this is the third time it's showed up in the Newest RSS feed