DEV Community

Cover image for Stripe / Supabase Billing Hell (And What I Did About It)
Emmett Miller
Emmett Miller

Posted on

2

Stripe / Supabase Billing Hell (And What I Did About It)

If you’ve ever tried adding billing to a Supabase project, you probably understand why I’m writing this.

I love Supabase. But when it came time to let users pay for something, everything kind of fell apart.

Stripe: The dream and the reality

Stripe is the go-to solution for payments. Everyone uses it. Tons of libraries. Clean dashboard. But integrating it? That’s where the pain begins.

Here’s what I needed just to get basic billing working:

  • Webhook server to handle Stripe events
  • Logic for trials, grace periods, downgrades
  • UI to show invoices, plans, and billing status
  • Mapping Stripe subscriptions to user permissions
  • Handling canceled payments and past-due states

Stripe might be the most documented API on the planet, but setup was still a slog.

So we built Update

We didn’t set out to build a Stripe wrapper. I just wanted a way to say:

"Only show this page if the user is on a paid plan."

Without it exploding into 17 webhook events and 400 lines of boilerplate.

So we built Update, a drop-in billing system that plays nicely with Supabase. It’s not a replacement. It fills in the parts Supabase doesn’t handle: billing, permissions, and (eventually) multi-tenancy.

This post isn’t a promotion. I just want to show how I wired it up.

How I integrated Update with Supabase

1. Initialized Update alongside Supabase

Update wraps the Supabase client and adds billing + entitlements:

import { createClient } from "@updatedev/js/supabase";

const client = createClient({
  process.env.NEXT_PUBLIC_UPDATE_PUBLIC_KEY!,
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
});
Enter fullscreen mode Exit fullscreen mode

All the usual Supabase functions still work:

const { data } = await client.from("profiles").select("*");
await client.auth.signInWithPassword(credentials)
Enter fullscreen mode Exit fullscreen mode

Update just layers on top.

2. Fetched plans and create checkout sessions

First, I fetched the available products:

const { data: productData, error } = await client.billing.getProducts();

const proPlan = productData.find(p => p.name === "Pro");
const priceId = proPlan?.prices?.[0]?.id;
Enter fullscreen mode Exit fullscreen mode

Then I created a checkout session for the selected plan:

const { data: session } = await client.billing.createCheckoutSession(
  priceId,
  {
    redirect_url: "http://localhost:3000/billing/success",
  }
);

window.location.href = session.url;
Enter fullscreen mode Exit fullscreen mode

No webhooks. No syncing with Stripe. Subscriptions are automatically kept up to date for me.

  1. Checked entitlements in the UI

Once subscribed, I can gate features like this:

const { data } = await client.entitlements.check("pro");

if (!data.hasAccess) {
  // show the premium UI
  redirect('/upgrade')
}
Enter fullscreen mode Exit fullscreen mode

This checks individual or organization level access based on payment status.

The result

I went from tangled webhook logic to a setup where billing just... works. Subscriptions, permissions, and entitlements update in real time. Stripe is still there—but abstracted away.

If you’re in the same boat, you might find Update useful. Or maybe you’ll just write another wrapper, like we did.

Either way, billing shouldn’t be this hard.

Tiugo image

Modular, Fast, and Built for Developers

CKEditor 5 gives you full control over your editing experience. A modular architecture means you get high performance, fewer re-renders and a setup that scales with your needs.

Start now

Top comments (0)

Neon image

Next.js applications: Set up a Neon project in seconds

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Get started →

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, valued within the supportive DEV Community. Coders of every background are welcome to join in and add to our collective wisdom.

A sincere "thank you" often brightens someone’s day. Share your gratitude in the comments below!

On DEV, the act of sharing knowledge eases our journey and fortifies our community ties. Found value in this? A quick thank you to the author can make a significant impact.

Okay