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.
| Stufe | Monatspreis | Jahrespreis | Credits/Monat | Feature-Level |
|---|---|---|---|---|
| Free | €0 | — | 500 | Basiszugriff, Community-Support |
| Basic | €9,90 | €99,00 | 1.500 | Kernfunktionen, E-Mail-Support |
| Pro | €19,90 | €199,00 | 5.000 | Erweiterte Funktionen, Prioritäts-Support |
| Enterprise | €39,90 | €399,00 | 15.000 | Alle Funktionen, dedizierter Support |
Preise und Credit-Zuteilungen sind über Umgebungsvariablen konfigurierbar. Die obige Tabelle zeigt die Standardwerte.
Enterprise ist eine ausschließlich auf Anfrage verfügbare Stufe — es gibt keine Self-Service-Jahres-Checkout-Variante. Enterprise-Kunden werden manuell oder über einen individuellen Vertriebsprozess bereitgestellt. Die monatliche Variante existiert für API-basierte Bereitstellung, wenn benötigt.
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:
| Umgebungsvariable | Stufe | Abrechnung |
|---|---|---|
NEXT_PUBLIC_LEMONSQUEEZY_BASIC_MONTHLY_VARIANT_ID | Basic | Monatlich |
NEXT_PUBLIC_LEMONSQUEEZY_BASIC_YEARLY_VARIANT_ID | Basic | Jährlich |
NEXT_PUBLIC_LEMONSQUEEZY_PRO_MONTHLY_VARIANT_ID | Pro | Monatlich |
NEXT_PUBLIC_LEMONSQUEEZY_PRO_YEARLY_VARIANT_ID | Pro | Jährlich |
NEXT_PUBLIC_LEMONSQUEEZY_ENTERPRISE_MONTHLY_VARIANT_ID | Enterprise | Monatlich |
Credit-basierte Varianten
Credit-basiertes Pricing verwendet 3 Variant-IDs — nur monatlich:
| Umgebungsvariable | Stufe | Abrechnung |
|---|---|---|
NEXT_PUBLIC_CREDIT_BASIC_MONTHLY_VARIANT_ID | Basic | Monatlich |
NEXT_PUBLIC_CREDIT_PRO_MONTHLY_VARIANT_ID | Pro | Monatlich |
NEXT_PUBLIC_CREDIT_ENTERPRISE_MONTHLY_VARIANT_ID | Enterprise | Monatlich |
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
}
| Funktion | Rückgabe | Verwendung |
|---|---|---|
getTierLevel(variantId) | 0-3 (0 = free, 1 = basic, 2 = pro, 3 = enterprise) | Upgrade-/Downgrade-Vergleich |
getTierName(variantId) | 'basic', 'pro', 'enterprise' oder null | UI-Anzeige, Feature-Gates |
getBillingPeriod(variantId) | 'monthly', 'yearly' oder null | Anzeige 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:
- Status ist
activeoderon_trial— vollständiger Zugriff unabhängig vom Ablaufdatum - Status ist
cancelled, abercurrentPeriodEndliegt 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:| Ressource | Free | Basic | Pro | Enterprise |
|---|---|---|---|---|
| Projekte | 1 | 5 | 20 | Unbegrenzt |
| Speicher (MB) | 100 | 1.000 | 10.000 | Unbegrenzt |
| API-Aufrufe | 1.000 | 10.000 | 100.000 | Unbegrenzt |
| Teammitglieder | 1 | 3 | 10 | Unbegrenzt |
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:- Sofortiger Zugriff — Der Nutzer erhält sofort die neue Stufe
- Anteilige Abrechnung — Lemon Squeezy berechnet die Differenz für den verbleibenden Zeitraum
- Credit-Anpassung — Im credit-basierten Modus erhält der Nutzer die Credit-Zuteilung der neuen Stufe
- Stufensynchronisierung — Das Feld
User.tierwird aktualisiert, um die neue Stufe widerzuspiegeln - Präferenz erhalten — Die
bonusCreditsAutoUse-Präferenz des Nutzers (Nutzer-Toggle für Bonus-Credits) ist von Stufenwechseln nicht betroffen
Upgrades treten sofort in Kraft. Der Nutzer muss nicht auf den nächsten Abrechnungszyklus warten. Lemon Squeezy berechnet den anteiligen Betrag automatisch basierend auf den verbleibenden Tagen im aktuellen Abrechnungszeitraum.
Downgrades
Downgrades werden auf das Ende des aktuellen Abrechnungszeitraums verschoben, damit Nutzer das bekommen, wofür sie bezahlt haben:
- Verzögerter Zugriff — Der Nutzer behält seine aktuelle Stufe bis
currentPeriodEnd - Nächster Zeitraum — Bei der Verlängerung berechnet Lemon Squeezy den niedrigeren Stufenpreis
- Credit-Deckelung — Im credit-basierten Modus werden Credits beim nächsten Reset auf die Zuteilung der neuen Stufe begrenzt
- Stufensynchronisierung —
User.tierwird aktualisiert, wenn dersubscription_updated-Webhook am Periodenende feuert
Während einer Testphase sind Nutzer auf 2 Downgrades beschränkt. Dies verhindert Missbrauch, bei dem Nutzer wiederholt zwischen kostenlosen Testphasen verschiedener Stufen wechseln. Das
hasUsedTrial-Flag im User-Modell verfolgt, ob eine Testphase bereits genutzt wurde.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:- Prüft, ob der Nutzer bereits
hasUsedTrial = truehat - Falls nicht, setzt
hasUsedTrial = trueim User-Datensatz - Erstellt das Abonnement mit dem Status
on_trial - 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
activeviasubscription_updated-Webhook - Fehlgeschlagene Zahlung — Status wechselt zu
expiredviasubscription_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
Das Kundenportal wird vollständig von Lemon Squeezy gehostet. In deiner Anwendung ist keine zusätzliche UI-Implementierung erforderlich — stelle einfach einen Link oder eine Weiterleitung zur Portal-URL bereit.