Back to roadmaps lemonsqueezy Course

Project: Multi-Tier SaaS Subscriptions with Data Sync

In this project, we will implement multi-tier SaaS memberships. Users can select between monthly or yearly subscription options, sign up via hosted checkouts, and trigger database role updates automatically via webhooks.


1. Webhook Role Mapping Rules

When mapping Lemon Squeezy variants to user roles inside your database, use a configuration object:

// src/config/subscriptionTiers.ts
export const subscriptionTiers = {
  "variant_11111": { name: "Pro Monthly", role: "pro", limit: 100 },
  "variant_22222": { name: "Pro Yearly", role: "pro", limit: 100 },
  "variant_33333": { name: "Enterprise Monthly", role: "enterprise", limit: 1000 },
};

2. Ingesting Upgrades and Plan Changes

When the subscription_updated webhook hits your route:

  1. Identify the new variant ID.
  2. Update the user role status in PostgreSQL to unlock relevant feature boundaries.
// app/api/webhooks/lemonsqueezy/route.ts (Subscription update snippet)
import { prisma } from "../../../lib/prisma";
import { subscriptionTiers } from "../../../config/subscriptionTiers";

export async function handleSubscriptionUpdate(event: any) {
  const data = event.data;
  const attributes = data.attributes;

  const subscriptionId = data.id;
  const variantId = String(attributes.variant_id);

  // 1. Find the mapped role configuration
  const tierConfig = subscriptionTiers[variantId as keyof typeof subscriptionTiers];

  if (!tierConfig) {
    console.error("Unknown variant ID received:", variantId);
    return;
  }

  // 2. Update the user account limits and roles
  await prisma.user.updateMany({
    where: { lemonSubscriptionId: subscriptionId },
    data: {
      pricingTier: tierConfig.role,
      projectLimit: tierConfig.limit,
      subscriptionActive: attributes.status === "active",
    },
  });

  console.log(`Updated user membership to: ${tierConfig.name}`);
}

3. Frontend Integration Validation

To verify the integration:

  • Open your billing settings page.
  • Select a pricing plan card.
  • After completing payment, confirm that your database user profile pricingTier switches to the upgraded state and the user is redirected to their Dashboard.
Published on Last updated: