Abonnement-Pläne

Preisstufen konfigurieren, Feature-Gates einrichten, Plan-Upgrades und -Downgrades verwalten und Abrechnungszeiträume festlegen

Kit enthält vier Abonnement-Stufen — Free, Basic, Pro und Enterprise. Jede Stufe ist Lemon Squeezy-Variant-IDs zugeordnet, und die Anwendung verwendet diese IDs, um Feature-Zugriff, Credit-Zuteilung und Abrechnungsverhalten zu bestimmen.
Diese Seite behandelt die Stufenkonfiguration, Planwechsel und das Trial-System. Für die Credit-Zuteilung pro Stufe siehe Credit-System. Für das anfängliche Lemon Squeezy-Setup siehe Setup.

Stufenkonfiguration

Jeder Nutzer startet auf der Free-Stufe. Bezahlte Stufen werden aktiviert, wenn ein Abonnement über Lemon Squeezy-Webhooks erstellt wird.
StufeMonatspreisJahrespreisCredits/MonatFeature-Level
Free€0500Basiszugriff, Community-Support
Basic€9,90€99,001.500Kernfunktionen, E-Mail-Support
Pro€19,90€199,005.000Erweiterte Funktionen, Prioritäts-Support
Enterprise€39,90€399,0015.000Alle Funktionen, dedizierter Support
Preise und Credit-Zuteilungen sind über Umgebungsvariablen konfigurierbar. Die obige Tabelle zeigt die Standardwerte.

Variant-ID-Mapping

Die Datei config.ts bildet Umgebungsvariablen-Variant-IDs auf eine strukturierte Konfiguration ab. Dies ist die zentrale Wahrheitsquelle für alle Stufenerkennungen in der Anwendung:
src/lib/payments/config.ts — Variant ID Mapping
export const variantIds: PricingTierConfig = {
  basic: {
    monthly: process.env.NEXT_PUBLIC_LEMONSQUEEZY_BASIC_MONTHLY_VARIANT_ID,
    yearly: process.env.NEXT_PUBLIC_LEMONSQUEEZY_BASIC_YEARLY_VARIANT_ID,
  },
  pro: {
    monthly: process.env.NEXT_PUBLIC_LEMONSQUEEZY_PRO_MONTHLY_VARIANT_ID,
    yearly: process.env.NEXT_PUBLIC_LEMONSQUEEZY_PRO_YEARLY_VARIANT_ID,
  },
  enterprise: {
    // Enterprise is contact-only tier (no direct checkout, no yearly variant)
    monthly: process.env.NEXT_PUBLIC_LEMONSQUEEZY_ENTERPRISE_MONTHLY_VARIANT_ID,
  },
}

/**
 * Credit-Based Model Variant IDs
 * Credit-based pricing only supports monthly billing (no yearly option)
 */
export const creditBasedVariantIds: PricingTierConfig = {
  basic: {
    monthly: process.env.NEXT_PUBLIC_CREDIT_BASIC_MONTHLY_VARIANT_ID,
    yearly: undefined, // Credit-based only supports monthly
  },
  pro: {
    monthly: process.env.NEXT_PUBLIC_CREDIT_PRO_MONTHLY_VARIANT_ID,
    yearly: undefined,
  },
  enterprise: {
    monthly: process.env.NEXT_PUBLIC_CREDIT_ENTERPRISE_MONTHLY_VARIANT_ID,
    yearly: undefined,
  },
}

Klassische SaaS-Varianten

Klassisches SaaS verwendet 6 Variant-IDs — monatlich und jährlich für jede bezahlte Stufe:
UmgebungsvariableStufeAbrechnung
NEXT_PUBLIC_LEMONSQUEEZY_BASIC_MONTHLY_VARIANT_IDBasicMonatlich
NEXT_PUBLIC_LEMONSQUEEZY_BASIC_YEARLY_VARIANT_IDBasicJährlich
NEXT_PUBLIC_LEMONSQUEEZY_PRO_MONTHLY_VARIANT_IDProMonatlich
NEXT_PUBLIC_LEMONSQUEEZY_PRO_YEARLY_VARIANT_IDProJährlich
NEXT_PUBLIC_LEMONSQUEEZY_ENTERPRISE_MONTHLY_VARIANT_IDEnterpriseMonatlich

Credit-basierte Varianten

Credit-basiertes Pricing verwendet 3 Variant-IDs — nur monatlich:
UmgebungsvariableStufeAbrechnung
NEXT_PUBLIC_CREDIT_BASIC_MONTHLY_VARIANT_IDBasicMonatlich
NEXT_PUBLIC_CREDIT_PRO_MONTHLY_VARIANT_IDProMonatlich
NEXT_PUBLIC_CREDIT_ENTERPRISE_MONTHLY_VARIANT_IDEnterpriseMonatlich

Stufenerkennung

Drei Funktionen in config.ts übernehmen die gesamte Stufenerkennung in der Anwendung. Sie prüfen beide Preismodelle automatisch:
src/lib/payments/config.ts — getTierLevel()
export function getTierLevel(variantId: string): number {
  // Check basic tier (Credit-Based first, then Classic SaaS)
  if (
    variantId === creditBasedVariantIds.basic.monthly ||
    variantId === variantIds.basic.monthly ||
    variantId === variantIds.basic.yearly
  ) {
    return 1
  }

  // Check pro tier (Credit-Based first, then Classic SaaS)
  if (
    variantId === creditBasedVariantIds.pro.monthly ||
    variantId === variantIds.pro.monthly ||
    variantId === variantIds.pro.yearly
  ) {
    return 2
  }

  // Check enterprise tier (Credit-Based first, then Classic SaaS)
  if (
    variantId === creditBasedVariantIds.enterprise.monthly ||
    variantId === variantIds.enterprise.monthly ||
    variantId === variantIds.enterprise.yearly
  ) {
    return 3
  }

  // Unknown variant ID - return 0 (free tier)
  return 0
}
FunktionRückgabeVerwendung
getTierLevel(variantId)0-3 (0 = free, 1 = basic, 2 = pro, 3 = enterprise)Upgrade-/Downgrade-Vergleich
getTierName(variantId)'basic', 'pro', 'enterprise' oder nullUI-Anzeige, Feature-Gates
getBillingPeriod(variantId)'monthly', 'yearly' oder nullAnzeige des Abrechnungszeitraums
Diese Funktionen prüfen zuerst credit-basierte Variant-IDs, dann klassische SaaS-Varianten. Das bedeutet, sie funktionieren korrekt unabhängig davon, welches Preismodell aktiv ist.

Abonnement-Stufen-Auflösung

Die Funktion getSubscriptionTier() in subscription.ts kombiniert die Variantenerkennung mit dem Abonnement-Status, um die effektive Stufe eines Nutzers zu bestimmen:
typescript
import { getSubscriptionTier } from '@/lib/payments/subscription'

// In einer Server-Komponente oder API-Route
const subscription = await getUserSubscription(userId)
const tier = getSubscriptionTier(subscription)
// Gibt zurück: 'free' | 'basic' | 'pro' | 'enterprise'
Ein Abonnement gewährt Zugriff, wenn:
  1. Status ist active oder on_trial — vollständiger Zugriff unabhängig vom Ablaufdatum
  2. Status ist cancelled, aber currentPeriodEnd liegt in der Zukunft — Gnadenfrist-Zugriff
Das bedeutet, gekündigte Nutzer behalten ihre bezahlten Features bis zum Ende ihres Abrechnungszeitraums.

Feature-Gates

Verwende die Stufe, um Features in deiner Anwendung bedingt zu aktivieren:
typescript
import { getSubscriptionTier } from '@/lib/payments/subscription'
import { getUserSubscription } from '@/lib/payments/subscription'

// Beispiel Server-Komponente
export default async function DashboardPage() {
  const subscription = await getUserSubscription(userId)
  const tier = getSubscriptionTier(subscription)

  return (
    <div>
      {/* Feature für alle bezahlten Stufen verfügbar */}
      {tier !== 'free' && <AdvancedAnalytics />}

      {/* Feature nur für Pro und Enterprise */}
      {['pro', 'enterprise'].includes(tier) && <PrioritySupport />}

      {/* Nur Enterprise-Feature */}
      {tier === 'enterprise' && <CustomIntegrations />}
    </div>
  )
}

Abonnement-Limits

Kit definiert Standard-Ressourcenlimits pro Stufe. Diese werden von getSubscriptionLimits() zurückgegeben:
RessourceFreeBasicProEnterprise
Projekte1520Unbegrenzt
Speicher (MB)1001.00010.000Unbegrenzt
API-Aufrufe1.00010.000100.000Unbegrenzt
Teammitglieder1310Unbegrenzt
Passe diese Limits in apps/boilerplate/src/lib/payments/subscription.ts an das Feature-Set deines Produkts an.

Planwechsel

Upgrades

Wenn ein Nutzer auf eine höhere Stufe upgradet, verwaltet Lemon Squeezy die Abrechnungsanteilung automatisch. Kit verarbeitet das Upgrade über den subscription_updated-Webhook:
  1. Sofortiger Zugriff — Der Nutzer erhält sofort die neue Stufe
  2. Anteilige Abrechnung — Lemon Squeezy berechnet die Differenz für den verbleibenden Zeitraum
  3. Credit-Anpassung — Im credit-basierten Modus erhält der Nutzer die Credit-Zuteilung der neuen Stufe
  4. Stufensynchronisierung — Das Feld User.tier wird aktualisiert, um die neue Stufe widerzuspiegeln
  5. Präferenz erhalten — Die bonusCreditsAutoUse-Präferenz des Nutzers (Nutzer-Toggle für Bonus-Credits) ist von Stufenwechseln nicht betroffen

Downgrades

Downgrades werden auf das Ende des aktuellen Abrechnungszeitraums verschoben, damit Nutzer das bekommen, wofür sie bezahlt haben:
  1. Verzögerter Zugriff — Der Nutzer behält seine aktuelle Stufe bis currentPeriodEnd
  2. Nächster Zeitraum — Bei der Verlängerung berechnet Lemon Squeezy den niedrigeren Stufenpreis
  3. Credit-Deckelung — Im credit-basierten Modus werden Credits beim nächsten Reset auf die Zuteilung der neuen Stufe begrenzt
  4. StufensynchronisierungUser.tier wird aktualisiert, wenn der subscription_updated-Webhook am Periodenende feuert

Abrechnungszeitraum-Umschalten

Nutzer im klassischen SaaS können zwischen monatlicher und jährlicher Abrechnung wechseln. Die Funktion toggleBillingPeriod() löst die korrekte Variant-ID für den Wechsel auf:
src/lib/payments/config.ts — toggleBillingPeriod()
export function toggleBillingPeriod(
  variantId: string,
  toYearly: boolean
): string | null {
  const tierName = getTierName(variantId)

  if (!tierName) {
    return null
  }

  const tier = variantIds[tierName as keyof PricingTierConfig]

  if (toYearly) {
    return tier.yearly || null
  } else {
    return tier.monthly || null
  }
}
Die Funktion ermittelt die aktuelle Stufe anhand der Variant-ID und gibt dann die entsprechende monatliche oder jährliche Variante zurück. Wenn die Stufe keine jährliche Variante hat (wie Enterprise), gibt sie null zurück.

Trial-System

Kit unterstützt kostenlose Testphasen mit integriertem Missbrauchsschutz:

Konfiguration

Testphasen werden durch Setzen der Umgebungsvariable TRIAL_DAYS aktiviert:
bash
# 14-tägige Testphase aktivieren
TRIAL_DAYS="14"

# Testphasen deaktivieren (auf 0 setzen oder weglassen)
TRIAL_DAYS="0"

Trial-Tracking

Wenn ein Nutzer ein Test-Abonnement startet, führt der subscription_created-Webhook-Handler Folgendes aus:
  1. Prüft, ob der Nutzer bereits hasUsedTrial = true hat
  2. Falls nicht, setzt hasUsedTrial = true im User-Datensatz
  3. Erstellt das Abonnement mit dem Status on_trial
  4. Initialisiert Credits auf die Zuteilung der ausgewählten Stufe

Trial-Konvertierung

Am Ende der Testphase verwaltet Lemon Squeezy die Konvertierung:
  • Erfolgreiche Zahlung — Status wechselt zu active via subscription_updated-Webhook
  • Fehlgeschlagene Zahlung — Status wechselt zu expired via subscription_expired-Webhook
  • Kündigung während der Testphase — Status wechselt zu cancelled, Gnadenfrist gilt
Das hasUsedTrial-Flag bleibt dauerhaft bestehen. Ein Nutzer, der während einer Testphase kündigt, kann keine weitere Testphase auf einer anderen Stufe starten.

Kundenportal

Lemon Squeezy stellt ein gehostetes Kundenportal für Self-Service-Abonnementverwaltung bereit. Nutzer können ihre Zahlungsmethode aktualisieren, Rechnungen einsehen und ihr Abonnement kündigen.

Portal-URL-Generierung

Kit generiert eine signierte Portal-URL über die Lemon Squeezy API. Die URL ist 24 Stunden gültig:
typescript
import { getCustomerPortal } from '@/lib/payments/subscription'

// In einer API-Route oder Server Action
const { portalUrl } = await getCustomerPortal(userId)
// Nutzer zur portalUrl weiterleiten
Die Portal-URL wird aus dem urls.customer_portal-Attribut des Abonnements extrahiert, das Lemon Squeezy für jede API-Anfrage vorabbzeichnet.

Portal-Funktionen

Über das Portal können Nutzer:
  • Rechnungen einsehen und herunterladen
  • Zahlungsmethode aktualisieren (Kreditkarte, PayPal)
  • Ihr Abonnement kündigen
  • Abrechnungsverlauf einsehen
  • Rechnungsadresse aktualisieren