Stripe Subscription Integration in Node.js [2024 Ultimate Guide]

ivanivanovv

Ivan Ivanov

Posted on November 21, 2024

Stripe Subscription Integration in Node.js [2024 Ultimate Guide]

Getting Stripe subscriptions working with backend services can be tricky and often leads to what developers call the dreaded “brain split” - managing both Stripe's logic and your own backend data in sync.

At Vratix, we’ve tackled this problem head-on while building our Open Source Stripe Subscriptions API Module. Here's how we approach Stripe subscription billing in Node.js to keep things simple, scalable, and developer-friendly.

Core Principle: Let Stripe Be the Source of Truth

The key is to shift as much of the logic to Stripe while keeping your database minimal. We only store:

  • Customer ID
  • Subscription ID
  • Plan

This way, we avoid:

  • Overcomplicated backend logic
  • Error-prone webhook implementations for syncing dashboard changes
  • Data redundancy

With this approach, you still have a fully functional subscription billing system while relying on Stripe as the single source of truth.

Features of Our Implementation

By the end of this guide, you’ll have a subscription-based app supporting:

  • User subscription plans
  • Checkout sessions
  • Subscription upsells
  • Available plan listing

Tech Stack

  • PostgreSQL
  • Node.js + Express.js
  • TypeScript

Step 1: Database Design

We start by designing a clean, minimal database table:

CREATE TABLE user_subscriptions (  
    "id" SERIAL PRIMARY KEY,  
    "plan" VARCHAR NOT NULL,  
    "user_id" INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,  
    "customer_id" VARCHAR,  
    "subscription_id" VARCHAR NOT NULL,  
    "is_owner" BOOLEAN NOT NULL DEFAULT TRUE,  
    "created_at" TIMESTAMP NOT NULL DEFAULT NOW(),  
    UNIQUE (user_id, subscription_id)  
);
Enter fullscreen mode Exit fullscreen mode

Key points:

  • user_id: References your internal user table
  • plan: Tracks the subscription plan
  • subscription_id: The Stripe subscription ID
  • is_owner: Flags the primary subscription holder

Step 2: Controllers

We use a factory function to keep the business logic modular and testable. Here's a snippet from our Stripe Subscription Controller:

async getSubscriptions() {  
  const stripePrices = await stripe.prices.list({  
    active: true,  
    type: "recurring",  
    expand: ["data.product"],  
  });  

  return stripePrices.data.map((price) => {  
    const product = price.product as Stripe.Product;  
    return {  
      plan: price.lookup_key || product.name.toLowerCase().replaceAll(" ", "_"),  
      name: product.name,  
      priceId: price.id,  
      interval: price.recurring!.interval,  
      price: { currency: price.currency, amount: price.unit_amount },  
    };  
  });  
}  
Enter fullscreen mode Exit fullscreen mode

Key highlights:

  • Custom subscription keys: Derived from the product name or lookup_key for clean plan checks (user.plan === 'pro_plan').
  • Stripe-first approach: We fetch subscription data directly from Stripe, avoiding the “brain split.”

Step 3: Streamlined Stripe Checkout

Our createCheckout function sets up a subscription checkout session:

const checkout = await stripe.checkout.sessions.create({  
  line_items: [  
    {  
      price: priceId,  
      adjustable_quantity: { enabled: true },  
      quantity: seats || 1,  
    },  
  ],  
  mode: "subscription",  
  subscription_data: { metadata: { userId } },  
  success_url: CHECKOUT_SUCCESS_URL,  
  cancel_url: CHECKOUT_CANCEL_URL,  
});  

return { url: checkout.url! };  
Enter fullscreen mode Exit fullscreen mode

Want to Skip All This?

We’ve packaged everything into a ready-to-go Open Source module. In less than 30 seconds, you can set up:

  • Stripe integration
  • Authentication
  • Database configuration
  • Prebuilt routes and SQL queries

Run this:

npx vratix init  
Enter fullscreen mode Exit fullscreen mode

Check out our Stripe Subscriptions Module Docs for more details.

The full code is available on our GitHub repo.

See a demo video how to do all of this with a working UI here.

I’d love to hear your thoughts - does this make building subscription APIs easier? Let us know what features you’d like to see next!

💖 💪 🙅 🚩
ivanivanovv
Ivan Ivanov

Posted on November 21, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related