Stripe-Integration & Kundenabrechnung
Schritt-für-Schritt: vom leeren Workspace zur ersten Stripe-Rechnung, automatisch erstellt aus einem akzeptierten MSPercury-Angebot. Inklusive Behandlung von einmaligen Setup-Pauschalen plus monatlichen / jährlichen Vertragspositionen, USt-IdNr / VAT / EIN / GST-HST und EU-Reverse-Charge.
💡 Wichtig vorweg. MSPercury nutzt nicht Stripe Connect oder „on-behalf-of”-Buchungen. Du verbindest dein eigenes Stripe-Konto mit deinem eigenen API-Key. Geld fließt direkt zwischen dir und deiner Kundschaft — MSPercury sieht nur die API-Antwort, nie das Geld, nie eine Provision, nie einen Markup. Wir sind reiner Datentransporter.
1. Stripe-Konto vorbereiten
Falls du noch kein Stripe-Konto hast: stripe.com/de → Konto anlegen, KYC durchlaufen (Personalausweis + Bankverbindung). Dauert 5–15 Minuten. Bestehendes Konto? Springe direkt zu Schritt 2.
Stripe Tax aktivieren (sehr empfohlen)
Bevor du Rechnungen über MSPercury erstellst, schalte Stripe Tax im Stripe-Dashboard scharf:
- Stripe-Dashboard → More → Tax → Settings → Get started
- Ursprungs-Adresse setzen (deine Geschäftsadresse, gleich wie in MSPercury → Einstellungen → Workspace)
- Steuerregistrierungen hinzufügen — für DE-MSPs: deine deutsche USt-IdNr (
DEXXXXXXXXX) + Steuersatz 19 % automatisch - Optional: weitere Länder, in denen du registriert bist (Österreich, Schweiz, …)
🟡 Was Stripe Tax dir abnimmt. Korrekte USt-Berechnung bei innerdeutschen Rechnungen (19 %), automatischer Reverse-Charge bei EU-B2B-Empfängern mit gültiger USt-IdNr (0 %, Annotation auf der Rechnung), Drittland-Rechnungen mit 0 %. Ohne Stripe Tax musst du das alles manuell richten. Stripe Tax kostet 0,5 % pro abgerechneter Transaktion — bei einer 100-€-Rechnung also 50 Cent.
Restricted Key erstellen (statt Secret Key)
Statt deines vollen Secret Keys empfehlen wir dringend einen Restricted Key mit minimalen Rechten. Erstelle ihn so:
- Stripe-Dashboard → Developers → API keys → + Create restricted key
- Name:
MSPercury – Quote-Push(oder ähnlich) - Permissions setzen:
- Customers:
Write - Invoices:
Write - Subscriptions:
Write - Products:
Write - Prices:
Write - Tax IDs (Customers):
Write - Tax Rates:
Read - Charges, Payouts, Balance, Reporting: ❌ alles auf
Nonelassen
- Customers:
- Speichern → der Key beginnt mit
rk_test_…(Test-Modus) oderrk_live_…(Live-Modus)
🔒 Warum ein Restricted Key? Falls dein MSPercury-Workspace jemals kompromittiert wird (ungeschützter Admin-Account, gestohlenes Passwort), kann ein Angreifer mit einem Restricted Key keine Auszahlungen auslösen, keine Refunds verschieben, keine API-Keys rotieren. Er kann nur das tun, was MSPercury ohnehin macht: Rechnungen erstellen. Ein voller Secret Key (
sk_live_…) hingegen erlaubt im Wesentlichen alles — vermeide den, wenn möglich.
⚠️ Test-Modus zuerst. Mach dein Onboarding immer mit
rk_test_…durch. Stripe-Test-Rechnungen verschicken keine echten E-Mails an deine Kundschaft, ziehen kein echtes Geld ab, schreiben keine echte Buchhaltung mit. Wechsel aufrk_live_…erst, wenn der erste Test-Run sauber durchgelaufen ist.
2. Workspace-Steuerstammdaten setzen
Bevor du den Key in MSPercury einträgst, sind zwei Felder unter Einstellungen → Workspace wichtig:
- Land (Country): aus dem Dropdown wählen (z. B. Deutschland)
- Steuer-ID-Typ: füllt sich automatisch wenn du das Land änderst (DE →
eu_vat, US →us_ein, CA →ca_gst_hst) - USt-IdNr / Steuer-ID: deine eigene
DEXXXXXXXXXfür deutsche MSPs
💡 Was passiert mit diesen Feldern? Sie erscheinen im Footer jeder PDF (Angebot, Vereinbarung, CheckUp-Bericht), automatisch mit dem im Land üblichen Label: „USt-IdNr.: DEXXX” für deutsche Workspaces, „VAT ID: ATXXX” für österreichische, „EIN: 12-3456789” für US-MSPs, „GST/HST: XXXXXXXXXRT0001” für kanadische. Kein hartcodiertes Deutsch mehr. Außerdem werden sie an Stripe weitergereicht für Stripe-Tax-Jurisdiktions-Auflösung.
3. Stripe-Key in MSPercury eintragen
- Einstellungen → Integrationen öffnen
- Restricted Key (
rk_test_…für den ersten Versuch) ins Feld einfügen - Stripe-Konto verbinden klicken
Was passiert technisch:
- MSPercury ruft
stripe.accounts.retrieve()mit deinem Key auf, um zu prüfen dass der Key gültig ist und Account-Daten zu fetchen - Bei Erfolg: Key wird AES-256-GCM verschlüsselt at rest abgelegt (gleicher Helper wie für TOTP-Secrets), Account-Display-Name + ID + Mode (test/live) werden cached
- Bei Fehler: Stripe-Fehlermeldung wird 1:1 angezeigt (z. B. „No such account” → Key falsch, „Insufficient permissions” → Restricted Key hat zu wenige Scopes)
Die Status-Pille im Settings-Block zeigt dir:
- 🟢 Verbunden +
LIVE-Badge — alles bereit für echte Rechnungen - 🟡 Verbunden +
TEST-Badge — Sandbox-Modus, keine echten E-Mails / Belastungen - ⚪ Nicht verbunden — kein Key gesetzt
🔑 Key rotieren / ersetzen. Im verbundenen Zustand siehst du einen aufklappbaren „Key ersetzen”-Bereich. Paste neuen Key → der alte wird überschrieben, ein neuer
accounts.retrieve()-Call validiert. Wenn dein Restricted Key kompromittiert sein könnte (gestohlenes Notebook, durchgesickertes Repo): direkt ersetzen, dann bei Stripe den alten Key revozieren.
4. Kunden-Steuerstammdaten setzen
Pro Kunde (/customers/[id]/edit):
- Land: ISO-Code aus dem Dropdown
- Steuer-ID-Typ: meist auto-gefüllt vom Land (DE→
eu_vat, US→us_ein, CA→ca_gst_hst) - Steuer-ID-Wert: die tatsächliche Nummer wie
DE123456789,12-3456789,123456789RT0001 - Adresse: vollständig mit
Straße + PLZ + Stadt
💡 Warum so granular? Ohne Land + Tax-ID-Typ kann Stripe Tax die Jurisdiktion nicht auflösen. Ergebnis: 0 % USt auf Rechnungen, weil Stripe nicht weiß ob es sich um eine inländische Lieferung (19 % MwSt) oder eine EU-Reverse-Charge-Lieferung (0 %, vom Empfänger zu erklären) handelt. Beide sehen aus Stripe-Sicht ohne Land-Info gleich aus.
⚠️ Beim Stripe-Push. MSPercury sucht zuerst per
customers.search({ query: 'metadata.mspercury_customer_id:"…"' })ob der Kunde schon im Stripe-Konto existiert. Falls ja: Adresse + Tax-ID werden synchronisiert. Falls nein: ein neuer Stripe-Customer wird mittax_id_dataund Adresse angelegt. Mehrfaches Pushen erzeugt also keine Duplikate.
5. Angebot bauen und akzeptieren
Schau auf Getting Started wie ein Angebot von Null entsteht. Wichtig für die Stripe-Abrechnung sind die Abrechnungsperioden der Positionen:
| MSPercury-Periode | Stripe-Mapping | Wann verschickt |
|---|---|---|
| Einmalig | Invoice (allein) ODER InvoiceItem auf der ersten Sub-Rechnung (kombiniert) | Sofort, einmal |
| Monatlich | Subscription mit interval=month | Erste Rechnung sofort, dann jeden Monat |
| Jährlich | Subscription mit interval=year | Erste Rechnung sofort, dann jährlich |
💡 Setze die Periode bewusst. Wenn du eine Setup-Pauschale hast, sollte die als „einmalig” laufen. Eine RMM-Lizenz ist „monatlich”. Eine Backup-Lizenz mit Jahresvertrag ist „jährlich”. Im Angebots-Editor kannst du pro Position frei wählen. Falls du dich vertippst: nach dem Akzeptieren ist Edit-Post-Accept verfügbar (Live-Record + MRR werden synchronisiert; das signierte PDF bleibt als Audit-Referenz unverändert).
Sobald die Kundschaft das Angebot über die Share-Page akzeptiert (oder du es manuell auf akzeptiert setzt), wird der violette Stripe-Block sichtbar.
6. Stripe-Push — was wirklich passiert
Auf der Detailseite eines akzeptierten Angebots erscheint jetzt der violette „Stripe-Rechnung erstellen”-Block. Davor zeigt MSPercury dir explizit, was angelegt wird:
Wird in Stripe angelegt
├─ [Subscription] 6× monatlich wiederkehrend + 2× einmalig auf der ersten Rechnung
│ — Folgerechnungen rotieren automatisch
└─ [Subscription] 1× jährlich wiederkehrend
Klick auf den Button führt aus:
- Stripe-Customer find-or-create (mit Adresse + Tax-ID-Data)
- Pro nicht-leerer Rechnungsperiode:
subscriptions.create()mit den passenden Items - Einmalige Positionen: werden via
add_invoice_itemsan die erste Rechnung der ersten Subscription gehängt — Kundschaft bekommt eine Mail mit Setup + erstem Monat zusammen - Footer auf jeder Stripe-Rechnung: „MSPercury reference: Q-2026-XXXX” — verlinkt das Angebot per Auge
Was Stripe danach autonom macht:
- Rotiert die monatlichen / jährlichen Folgerechnungen automatisch (kein zweiter API-Call von uns)
- Verschickt Hosted-Invoice-Mails aus deinem Branding (Logo + Farbe im Stripe-Dashboard setzen!)
- Lässt die Kundschaft per Self-Service Zahlungsmethode ändern, kündigen, alte Rechnungen herunterladen
- Wendet Stripe-Tax-Logik bei jeder Folgerechnung an (Reverse-Charge bleibt aktiv solange die Tax-ID gültig ist)
🟢 „Sofort schicken”-Toggle. Bei reinen Einmal-Angeboten kannst du wählen: Draft im Stripe-Dashboard liegen lassen oder direkt versenden. Bei Subscription-Angeboten immer sofortiger Versand — Stripe finalisiert + emailt die erste Rechnung beim
subscriptions.create()-Call. Es gibt kein „Draft-Subscription”-Konzept in der Stripe-API. Falls du das nicht willst: nicht klicken, oder im Stripe-Dashboard die Subscription wieder pausieren / löschen.
🟡 Mengenangabe-Constraint. Stripe akzeptiert für Subscription-Items nur ganzzahlige Mengen (
quantitymussintsein). Quote-Lines mit fraktionalen Mengen (z. B. „1.5 Stunden”) werden auf den nächsten Integer gerundet (Math.round(...)). Für Stunden-Pauschalen empfehlen wir: Stunden in Cent oder Minuten umrechnen (z. B. „90 Minuten” als Menge mit „pro Minute”-Einheitspreis), oder eine eigene „Pauschale 1.5 h”-Zeile.
7. Reverse-Charge bei EU-B2B
Stripe Tax wendet die Reverse-Charge-Regel automatisch an, vorausgesetzt:
- Dein Workspace ist mit Land + USt-IdNr in DE/EU registriert
- Der Kunde ist in einem anderen EU-Mitgliedstaat (also nicht innerdeutsch)
- Der Kunde hat einen Tax-ID-Typ
eu_vatmit gültiger Nummer (z. B.FR12345678901)
In dem Fall:
- Stripe-Rechnung: 0 % MwSt, Annotation „Reverse charge — Art. 196 VAT Directive”
- Quote-PDF aus MSPercury: zeigt zusätzlich den gelben Reverse-Charge-Banner (Art. 196 MwSt-RL / §13b UStG, lokalisiert in der Kundensprache)
💡 Was wenn der Kunde keine USt-IdNr hat? Dann ist es keine B2B-Reverse-Charge-Konstellation, sondern eine B2C-Lieferung — Stripe wendet deinen lokalen Steuersatz an (DE 19 %), du musst die USt im Empfängerland selbst abführen (One-Stop-Shop / OSS-Verfahren). Das ist meistens nicht das, was du mit MSP-Kunden willst. Wenn du den Reverse-Charge-Pfad brauchst, frag aktiv nach der USt-IdNr — und prüf sie über das VIES-Tool bevor du sie einträgst.
⚠️ CH und UK. Schweiz (
ch_vat) und UK (gb_vat) sind nicht EU — Reverse-Charge greift nicht. Stripe Tax muss separat für diese Länder konfiguriert werden, falls du dort viele Kunden hast.
8. Häufige Stolpersteine
| Symptom | Ursache | Lösung |
|---|---|---|
| Stripe lehnt den Key ab beim Speichern | Key falsch oder Restricted Key hat zu wenige Rechte | Permissions im Stripe-Dashboard prüfen, neuen Restricted Key mit den oben genannten Scopes erstellen |
| Erste Rechnung kommt mit 0 % USt obwohl innerdeutsch | Workspace hat kein Land / Tax-ID-Typ gesetzt | Einstellungen → Workspace → Land + Steuer-ID-Typ ergänzen |
| Reverse-Charge wird nicht angewendet | Kunde hat kein eu_vat-Type oder leere Tax-ID | Customer-Edit → Land + Type + Tax-ID-Wert komplett füllen, dann erneut pushen |
| Doppelte Rechnung beim zweiten Klick | Idempotenz noch nicht implementiert (Roadmap) | Im Stripe-Dashboard die Duplikat-Subscription / -Rechnung manuell stornieren |
| „Customer hat keine E-Mail-Adresse” | E-Mail-Feld am MSPercury-Kunden leer | Kunde-Edit → E-Mail eintragen — Stripe braucht zwingend eine zum Versand der Hosted-Invoice |
| Rechnung verschickt aber Kundschaft beklagt fehlende Mail | Stripe-Tax oder Stripe-Account zickt mit Hosted-Invoice | Stripe-Dashboard → Invoices → die Rechnung auswählen → „Send again” |
| Subscriptions-erste-Rechnung landet zwar an, aber Kunde glaubt das ist ALLES | Stripe-Dashboard → Subscriptions → Footer der Hosted-Invoice individualisieren | In Stripe Tax / Branding den Begleittext anpassen, der auf Folgerechnungen hinweist |
9. Was MSPercury nicht macht
Klare Abgrenzung damit du weißt wo Stripe und wo wir die Verantwortung haben:
- ❌ MSPercury erstellt keine Steuerbescheinigungen, Jahresabschlüsse, USt-Voranmeldungen — das macht weiterhin dein Steuerberater oder dein Buchhaltungs-Tool (DATEV-Schnittstelle steht auf der Roadmap)
- ❌ MSPercury prüft keine USt-IdNr auf Gültigkeit — du bist verantwortlich für VIES-Validierung bevor du sie in MSPercury einträgst
- ❌ MSPercury stornieren keine Stripe-Rechnungen — wenn ein Kunde abspringt, im Stripe-Dashboard die Subscription pausieren / kündigen + die offene Rechnung void-en
- ❌ MSPercury archiviert keine Stripe-Rechnungen — die liegen in deinem Stripe-Konto, du bist für GoBD-konforme Aufbewahrung selbst zuständig (Stripe hat Export-Features)
- ❌ MSPercury bearbeitet keine Refund-Anfragen, Chargebacks, Disputes — alles im Stripe-Dashboard
- ❌ MSPercury kennt nicht dein Stripe-Saldo, deine Auszahlungen, deine Steuerlast — wir sehen nur Rechnungs-IDs, keine Geldflüsse
Was MSPercury macht:
- ✅ Aus jedem akzeptierten Angebot eine Stripe-Rechnung (oder Subscription) auf deinem Konto erstellen
- ✅ Customer-Stammdaten + Tax-IDs in Stripe synchronisieren bei jedem Push
- ✅ Footer / Metadata auf jeder Stripe-Rechnung mit Angebotsreferenz versehen — du kannst im Stripe-Dashboard zurück zum Angebot springen
- ✅ Den verschlüsselten API-Key sicher in deinem Workspace speichern, nie an Dritte rausgeben
10. Roadmap
Was noch kommt:
- Idempotenz: zweiter Klick auf „Stripe-Rechnung erstellen” wird erkannt, kein Duplikat
- Stripe-Subscription-IDs auf der Quote-Row: damit
/quotes/[id]zeigen kann „bereits an Stripe geschickt — ID acct_…”, inkl. Direkt-Link ins Stripe-Dashboard - Webhook-Sync: wenn die Stripe-Subscription gekündigt wird, MRR im Dashboard automatisch aktualisieren
- Lexware Office, sevDesk, Polar.sh: alternative Connectors für MSPs die nicht Stripe nutzen — siehe Roadmap
Wenn dir was Konkretes fehlt: Discord-Community, Channel #stripe-billing. Lucas liest mit.