Zum Hauptinhalt springen
Zurück zum Blog
Abonnements Stripe Zahlungen SaaS

Abonnements implementieren: Technischer Leitfaden

Vollständiger technischer Leitfaden zur Implementierung von Abonnement-Billing mit Stripe. Webhooks, Proration, Dunning und Steuer-Compliance.

JM
Javier Manzano
18. Juni 2026

Abonnement-Billing ist technisch komplexer als es zunächst erscheint. Einfache wiederkehrende Zahlungen sind schnell implementiert — aber ein produktionsreifes Billing-System mit Trials, Upgrades, Downgrades, fehlgeschlagenen Zahlungen und Steuer-Compliance erfordert Wochen sorgfältiger Entwicklung.

Dieser Leitfaden zeigt die wichtigsten Konzepte und Implementierungsdetails am Beispiel von Stripe Billing — dem De-facto-Standard für SaaS-Abonnements.

Kernkonzepte in Stripe Billing

Customers, Products, Prices und Subscriptions

// Produkt erstellen (einmalig)
const product = await stripe.products.create({
  name: 'Pro Plan',
  description: 'Unbegrenzte Projekte und Priority Support',
});

// Preis erstellen (wiederkehrend)
const price = await stripe.prices.create({
  product: product.id,
  unit_amount: 4900, // 49,00 EUR in Cent
  currency: 'eur',
  recurring: {
    interval: 'month',
    interval_count: 1,
  },
  nickname: 'Pro Monthly',
});

// Abonnement erstellen
const subscription = await stripe.subscriptions.create({
  customer: customerId,
  items: [{ price: price.id }],
  trial_period_days: 14, // 14-tägige Testphase
  payment_behavior: 'default_incomplete', // Warten auf Zahlungsbestätigung
  payment_settings: {
    save_default_payment_method: 'on_subscription',
  },
  expand: ['latest_invoice.payment_intent'],
});

Trial-Perioden

// Mit Trial starten (kein Zahlungsmittel erforderlich)
const subscription = await stripe.subscriptions.create({
  customer: customerId,
  items: [{ price: priceId }],
  trial_end: Math.floor(Date.now() / 1000) + (14 * 24 * 60 * 60), // 14 Tage
  trial_settings: {
    end_behavior: { missing_payment_method: 'pause' }, // Nicht kündigen, sondern pausieren
  },
});

// Trial ohne Kreditkarte ermöglichen (Free Trial)
const subscription = await stripe.subscriptions.create({
  customer: customerId,
  items: [{ price: priceId }],
  trial_period_days: 14,
  payment_behavior: 'allow_incomplete',
  // Zahlungsmethode erst bei Trial-Ende anfordern
});

Webhooks: Das Herzstück des Billing-Systems

Webhooks sind der wichtigste Teil Ihrer Stripe-Integration. Alle wichtigen Billing-Events werden über Webhooks kommuniziert.

// Webhook-Handler (Express.js)
app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), async (req, res) => {
  const sig = req.headers['stripe-signature'];

  let event;
  try {
    event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET);
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  switch (event.type) {
    case 'customer.subscription.created':
      await handleSubscriptionCreated(event.data.object);
      break;

    case 'customer.subscription.updated':
      await handleSubscriptionUpdated(event.data.object);
      break;

    case 'customer.subscription.deleted':
      await handleSubscriptionCanceled(event.data.object);
      break;

    case 'invoice.payment_succeeded':
      await handlePaymentSucceeded(event.data.object);
      break;

    case 'invoice.payment_failed':
      await handlePaymentFailed(event.data.object);
      break;

    case 'customer.subscription.trial_will_end':
      await sendTrialEndingEmail(event.data.object);
      break;
  }

  res.json({ received: true });
});

async function handlePaymentFailed(invoice) {
  const subscription = await stripe.subscriptions.retrieve(invoice.subscription);
  const customer = await stripe.customers.retrieve(invoice.customer);

  // Benutzer in Datenbank auf 'past_due' setzen
  await db.users.update({
    where: { stripe_customer_id: customer.id },
    data: {
      subscription_status: 'past_due',
      access_level: 'restricted',
    }
  });

  // E-Mail mit Update-Payment-Link senden
  const portalSession = await stripe.billingPortal.sessions.create({
    customer: customer.id,
    return_url: `${BASE_URL}/dashboard`,
  });

  await sendEmail({
    to: customer.email,
    template: 'payment_failed',
    data: { portal_url: portalSession.url, amount: invoice.amount_due }
  });
}

Dunning: Fehlgeschlagene Zahlungen automatisch wiederholen

Dunning ist der automatische Prozess, fehlgeschlagene Zahlungen erneut zu versuchen. Stripe Smart Retries analysiert die Ausfallgründe und wählt den optimalen Wiederholungszeitpunkt.

// Stripe Billing-Einstellungen konfigurieren
// (im Stripe Dashboard unter Billing > Einstellungen)

// Empfohlene Dunning-Strategie:
// Tag 1: Erster fehlgeschlagener Versuch
// Tag 3: Erneuter Versuch
// Tag 7: Erneuter Versuch + E-Mail-Erinnerung
// Tag 14: Letzter Versuch + dringliche E-Mail
// Tag 21: Subscription kündigen, Zugang sperren

// Kunden-Portal für Self-Service-Zahlungsupdate
const portalSession = await stripe.billingPortal.sessions.create({
  customer: customerId,
  return_url: `${BASE_URL}/dashboard`,
  // Kunden können im Portal: Zahlungsmethode updaten, Abo kündigen,
  // Rechnungen ansehen, Plan wechseln
});

Plan-Wechsel und Proration

// Upgrade von Basic zu Pro mitten im Monat
async function upgradeSubscription(subscriptionId, newPriceId) {
  const subscription = await stripe.subscriptions.retrieve(subscriptionId);

  const updatedSubscription = await stripe.subscriptions.update(subscriptionId, {
    items: [{
      id: subscription.items.data[0].id,
      price: newPriceId,
    }],
    proration_behavior: 'create_prorations', // Anteilige Berechnung
    // Stripe berechnet automatisch:
    // - Gutschrift für nicht genutzte Zeit des alten Plans
    // - Rechnung für neue Zeit des neuen Plans
  });

  return updatedSubscription;
}

// Preview vor dem Upgrade anzeigen
async function previewUpgrade(subscriptionId, newPriceId) {
  const proration_date = Math.floor(Date.now() / 1000);

  const invoice = await stripe.invoices.retrieveUpcoming({
    customer: customerId,
    subscription: subscriptionId,
    subscription_items: [{
      id: currentItemId,
      price: newPriceId,
    }],
    subscription_proration_date: proration_date,
  });

  return {
    amount_due: invoice.amount_due,
    next_charge: invoice.next_payment_attempt,
  };
}

Steuer-Compliance mit Stripe Tax

// Automatische Steuerberechnung aktivieren
const subscription = await stripe.subscriptions.create({
  customer: customerId,
  items: [{ price: priceId }],
  automatic_tax: { enabled: true },
  // Stripe berechnet automatisch:
  // - MwSt. für EU-Kunden (inkl. OSS-Regelung)
  // - Sales Tax für US-Kunden
  // - GST für australische Kunden
  // Voraussetzung: Kundenadressen müssen vollständig sein
});

// Produkt als steuerpflichtig konfigurieren
await stripe.products.update(productId, {
  tax_code: 'txcd_10103001', // SaaS-Produkte in der EU
});

Customer Portal: Self-Service für Ihre Kunden

Das Stripe Customer Portal ermöglicht Ihren Kunden, ihr Abonnement selbst zu verwalten — ohne dass Sie diese Features selbst entwickeln müssen.

// Portal-Session erstellen
app.post('/api/billing/portal', requireAuth, async (req, res) => {
  const user = req.user;

  const session = await stripe.billingPortal.sessions.create({
    customer: user.stripe_customer_id,
    return_url: `${BASE_URL}/dashboard/billing`,
    // Konfigurierbar im Stripe Dashboard:
    // - Welche Plan-Wechsel erlaubt sind
    // - Ob Kündigung erlaubt ist (und wenn ja: mit welcher Verzögerung)
    // - Rechnungshistorie
  });

  res.json({ url: session.url });
});

Metered Billing (nutzungsbasierte Abrechnung)

Für APIs, KI-Services oder andere nutzungsbasierte Produkte:

// Nutzung melden
async function reportUsage(subscriptionItemId, quantity) {
  await stripe.subscriptionItems.createUsageRecord(subscriptionItemId, {
    quantity: quantity,
    timestamp: Math.floor(Date.now() / 1000),
    action: 'increment', // oder 'set' für absoluten Wert
  });
}

// Beispiel: Nach jedem API-Call
app.use('/api/', async (req, res, next) => {
  next();
  res.on('finish', async () => {
    if (req.user?.subscription_item_id) {
      await reportUsage(req.user.subscription_item_id, 1);
    }
  });
});

Abonnement-Billing korrekt zu implementieren erfordert Erfahrung mit den Edge Cases: Was passiert bei gleichzeitigem Upgrade und fehlgeschlagener Zahlung? Wie gehen Sie mit Refunds bei Stornierung mid-cycle um? Wenn Sie ein SaaS aufbauen und Billing richtig machen wollen, sprechen Sie mit unserem Team.

Nichts verpassen

JM

Javier Manzano

Leidenschaftlich für Technologie und Softwareentwicklung. Wir teilen Wissen und Erfahrungen, um anderen Entwicklern beim Wachsen zu helfen.

Hat Ihnen dieser Artikel gefallen?

Wenn Sie Hilfe bei Ihrem Entwicklungsprojekt brauchen, sind wir für Sie da.

Kostenloses Gespräch buchen →