Zahlungen – Übersicht

Duales Preismodell mit Lemon Squeezy — credit-basierte und klassische SaaS-Abonnement-Architektur

Kit enthält ein vollständiges Zahlungssystem, das auf Lemon Squeezy als Merchant of Record aufbaut. Das System unterstützt zwei Preismodelle — credit-basiert für KI-intensive Anwendungen und klassisches SaaS für traditionelle Abonnement-Stufen — umschaltbar mit einer einzigen Umgebungsvariable.
Diese Seite behandelt die Architektur und die grundlegenden Konzepte. Für Einrichtungsanweisungen siehe Lemon Squeezy Setup. Für die Plan-Konfiguration siehe Abonnement-Pläne.

Duale Preisarchitektur

Kit unterstützt zwei unterschiedliche Preismodelle, die zur Deployment-Zeit über die Umgebungsvariable NEXT_PUBLIC_PRICING_MODEL ausgewählt werden:
NEXT_PUBLIC_PRICING_MODEL
    |
    |--- "credit_based" ──> Nutzer kaufen Credits, die pro KI-Operation verbraucht werden
    |                        Monatliche Credit-Zuteilung pro Stufe
    |                        Bonus-Credit-Käufe als Aufladung
    |                        Kostenerfassung pro Operation
    |
    |--- "classic_saas" ──> Traditionelle Abonnement-Stufen
                             Monatliche und jährliche Abrechnungszeiträume
                             Feature-Gates pro Stufe
                             Unterstützung für Testzeiträume
Es ist immer nur ein Modell aktiv. Das Preiskonfigurationssystem prüft beim Start, ob alle erforderlichen Umgebungsvariablen für das aktive Modell vorhanden sind:
src/lib/pricing/config.ts — Dual Model Header
/**
 * Pricing Configuration - DUAL Model
 *
 * Central configuration system for the dual pricing model.
 * Supports both credit-based and classic SaaS pricing.
 *
 * IMPORTANT: Only ONE model can be active at a time based on NEXT_PUBLIC_PRICING_MODEL env var.
 */

Credit-basiertes Modell

Designed für KI-intensive Anwendungen, bei denen die Nutzung pro Nutzer variiert. Jede Stufe enthält eine monatliche Credit-Zuteilung (Free: 500, Basic: 1.500, Pro: 5.000, Enterprise: 15.000). Credits werden pro KI-Operation mit atomaren Datenbanktransaktionen abgezogen. Nutzer können Bonus-Credits als Aufladung kaufen. Nur monatliche Abrechnung — keine jährliche Option.

Klassisches SaaS-Modell

Traditionelles Abonnement-Pricing mit monatlichen und jährlichen Abrechnungszeiträumen. Jede Stufe schaltet eine Reihe von Features frei. Enthält Unterstützung für Testzeiträume mit einem hasUsedTrial-Flag zur Missbrauchsverhinderung. Die jährliche Abrechnung bietet einen Rabatt. Die Enterprise-Stufe ist nur auf Anfrage erhältlich (kein Self-Service-Checkout).

Zahlungsfluss

Jede Zahlung in Kit folgt diesem Ablauf — vom Checkout-Button bis zur Datenbankaktualisierung:
Nutzer klickt "Abonnieren"
    |
    v
Checkout-URL (von Lemon Squeezy gehostet)
    |--- custom_data: { userId: "db_user_id" }
    |--- variant_id: ausgewählte Plan-Variante
    |
    v
Lemon Squeezy verarbeitet die Zahlung
    |--- Übernimmt die Steuerberechnung (Merchant of Record)
    |--- Verwaltet die Zahlungsmethode
    |--- Erstellt das Abonnement
    |
    v
Webhook POST an /api/webhooks/lemonsqueezy
    |
    |--- 1. HMAC-SHA256-Signatur verifizieren
    |--- 2. Event-Typ parsen (subscription_created, etc.)
    |--- 3. userId aus custom_data extrahieren
    |--- 4. An Event-Handler weiterleiten
    |
    v
Datenbank aktualisiert
    |--- Abonnement-Datensatz erstellt/aktualisiert
    |--- Nutzer-Stufe synchronisiert
    |--- Credits initialisiert (credit-basiertes Modell)
    |--- Willkommens-E-Mail versandt (nicht-blockierend)
Das entscheidende Detail ist custom_data — beim Generieren der Checkout-URL übergibt Kit die Datenbank-Nutzer-ID. So weiß der Webhook-Handler, welchem Nutzer das Abonnement gehört.

Lemon Squeezy-Integration

Kit verwendet Lemon Squeezy als Zahlungsanbieter aus einem bestimmten Grund: Es fungiert als Merchant of Record. Das bedeutet, Lemon Squeezy übernimmt die gesamte Steuerberechnung, -erhebung und -abführung — du musst dir keine Gedanken über Mehrwertsteuer, Umsatzsteuer oder steuerliche Compliance in verschiedenen Ländern machen.
Die Integration verwendet das offizielle @lemonsqueezy/lemonsqueezy.js SDK mit Lazy Initialization:
src/lib/payments/lemonsqueezy-client.ts — API Client
import {
  lemonSqueezySetup,
  listProducts,
  listVariants,
  getProduct,
  getVariant,
  getSubscription,
  updateSubscription,
  cancelSubscription,
  createCheckout,
  type Variant,
  type Product,
  type Subscription,
  type Checkout,
  type NewCheckout,
} from '@lemonsqueezy/lemonsqueezy.js'
import crypto from 'crypto'

// Initialize Lemon Squeezy client lazily
let isInitialized = false

const initializeLemonSqueezy = () => {
  if (isInitialized) return

  const apiKey = process.env.LEMONSQUEEZY_API_KEY

  if (!apiKey) {
    throw new Error('LEMONSQUEEZY_API_KEY is not set in environment variables')
  }

  lemonSqueezySetup({
    apiKey,
    onError: (error) => {
      console.error('Lemon Squeezy API error:', error)
    },
  })

  isInitialized = true
}
Der Client kapselt alle SDK-Funktionen mit einem ensureInitialized-Guard, sodass der API-Key nur aus der Umgebung gelesen wird, wenn die erste Zahlungsoperation stattfindet — nicht beim Import. Dies verhindert Build-Fehler, wenn Umgebungsvariablen noch nicht gesetzt sind.

Kernkonzepte

Varianten

Lemon Squeezy verwendet Varianten, um verschiedene Preisoptionen innerhalb eines Produkts darzustellen. In Kit ist jede Kombination aus Stufe + Abrechnungszeitraum eine separate Variante:
  • Klassisches SaaS: 6 Varianten (Basic/Pro/Enterprise × Monatlich/Jährlich)
  • Credit-basiert: 3 Varianten (Basic/Pro/Enterprise × nur Monatlich)
Jede Variante hat eine eindeutige ID aus dem Lemon Squeezy-Dashboard, die als Umgebungsvariablen gespeichert wird.

Abonnements

Ein Abonnement verbindet einen Nutzer mit einer Variante. Kit speichert Abonnements in der Datenbank mit der Lemon Squeezy-Abonnement-ID, der Variant-ID, dem Status und den Abrechnungszeitraum-Daten. Der Abonnement-Status steuert den Feature-Zugriff in der gesamten Anwendung.

Stufen

Kit definiert vier Stufen — Free, Basic, Pro und Enterprise. Jeder Nutzer startet auf der Free-Stufe. Die Stufe wird anhand der Variant-ID des Abonnements mithilfe der zentralen Funktionen getTierLevel() und getTierName() bestimmt. Stufenwechsel lösen im credit-basierten Modell Credit-Anpassungen aus.

Checkout-URLs

Der Checkout wird vollständig von der gehosteten Checkout-Seite von Lemon Squeezy abgewickelt. Kit generiert Checkout-URLs mit der korrekten Variant-ID und übergibt custom_data mit der Datenbank-ID des Nutzers. Nach der Zahlung leitet Lemon Squeezy zurück zu deiner Anwendung und sendet Webhook-Events.

Verzeichnisstruktur

Das Zahlungssystem erstreckt sich über mehrere Verzeichnisse:
apps/boilerplate/src/
├── lib/
│   ├── payments/
│   │   ├── config.ts              # Variant-ID-Mapping, Stufen-Erkennung
│   │   ├── lemonsqueezy-client.ts # API-Client mit Lazy Init
│   │   ├── subscription.ts        # Abonnement-Verwaltung, Kundenportal
│   │   └── types.ts               # Gemeinsame Zahlungstypen
│   ├── pricing/
│   │   ├── config.ts              # Konfiguration des dualen Preismodells
│   │   └── types.ts               # Preistypen (PricingModel, PricingConfig)
│   ├── credits/
│   │   ├── config.ts              # Credit-System-Feature-Flag, Stufen-Standardwerte
│   │   ├── credit-manager.ts      # Atomare Abbuchungen, Rückerstattungen, Resets
│   │   ├── credit-costs.ts        # Kostendefinitionen pro Operation
│   │   └── initialization.ts      # Lazy Credit-Initialisierung
│   └── webhooks/
│       └── credit-helpers.ts      # Credit-Update-Logik für Webhooks
├── app/
│   ├── api/
│   │   └── webhooks/
│   │       └── lemonsqueezy/
│   │           ├── route.ts       # Webhook-Endpunkt mit Signaturverifizierung
│   │           ├── handlers/      # 11 modulare Event-Handler
│   │           ├── lib/           # Signaturverifizierung, Hilfsfunktionen
│   │           └── types.ts       # Webhook-Payload-Typen
│   └── (dashboard)/
│       └── dashboard/
│           └── billing/           # Abrechnungs-UI (Planauswahl, Portal)

Umgebungsvariablen

VariableErforderlichModellZweck
LEMONSQUEEZY_API_KEYJaBeideServerseitiger API-Key für das Lemon Squeezy SDK
LEMONSQUEEZY_STORE_IDJaBeideDein Lemon Squeezy Store-Bezeichner
LEMONSQUEEZY_WEBHOOK_SECRETJaBeideHMAC-Secret zur Webhook-Signaturverifizierung
NEXT_PUBLIC_PRICING_MODELJaBeidecredit_based oder classic_saas
NEXT_PUBLIC_LEMONSQUEEZY_BASIC_MONTHLY_VARIANT_IDJaClassicBasic-Stufe monatliche Variant-ID
NEXT_PUBLIC_LEMONSQUEEZY_BASIC_YEARLY_VARIANT_IDJaClassicBasic-Stufe jährliche Variant-ID
NEXT_PUBLIC_LEMONSQUEEZY_PRO_MONTHLY_VARIANT_IDJaClassicPro-Stufe monatliche Variant-ID
NEXT_PUBLIC_LEMONSQUEEZY_PRO_YEARLY_VARIANT_IDJaClassicPro-Stufe jährliche Variant-ID
NEXT_PUBLIC_LEMONSQUEEZY_ENTERPRISE_MONTHLY_VARIANT_IDJaClassicEnterprise-Stufe monatliche Variant-ID
NEXT_PUBLIC_CREDIT_BASIC_MONTHLY_VARIANT_IDJaCreditBasic-Stufe Credit-Variant-ID
NEXT_PUBLIC_CREDIT_PRO_MONTHLY_VARIANT_IDJaCreditPro-Stufe Credit-Variant-ID
NEXT_PUBLIC_CREDIT_ENTERPRISE_MONTHLY_VARIANT_IDJaCreditEnterprise-Stufe Credit-Variant-ID
CURRENCYNeinBeideWährungscode (Standard: EUR)
Die vollständige Referenz der Umgebungsvariablen einschließlich credit-spezifischer Variablen findest du unter Umgebungsvariablen.

Wichtige Dateien

DateiZweck
apps/boilerplate/src/lib/payments/config.tsVariant-ID-Mapping, getTierLevel(), getTierName(), getBillingPeriod()
apps/boilerplate/src/lib/payments/lemonsqueezy-client.tsAPI-Client, Signaturverifizierungs-Helper, Test-Modus-Erkennung
apps/boilerplate/src/lib/payments/subscription.tsAbonnement-Abfragen, getSubscriptionTier(), Kundenportal-URL
apps/boilerplate/src/lib/pricing/config.tsDualer Pricing-Config-Loader mit Validierung und Caching
apps/boilerplate/src/lib/credits/config.tsCredit-System-Feature-Flag (isCreditSystemEnabled())
apps/boilerplate/src/lib/credits/credit-manager.tsAtomare Credit-Abbuchungen mit SELECT FOR UPDATE
apps/boilerplate/src/lib/credits/credit-costs.tsKostentabelle pro Operation (17 Operationstypen)
apps/boilerplate/src/app/api/webhooks/lemonsqueezy/route.tsWebhook-Endpunkt — Signaturverifizierung und Event-Routing
apps/boilerplate/src/app/api/webhooks/lemonsqueezy/handlers/11 modulare Event-Handler (eine Datei pro Event)