Was ein Audit unseres eigenen Bezahlsystems zutage gefördert hat
Zwei kritische Befunde, und beide lagen nicht dort, wo wir gesucht hätten. Ein offener Bericht über Fehler, die inzwischen behoben sind.

Im August 2026 haben wir unser Bezahlsystem von Grund auf durchgesehen. Nicht weil etwas passiert war, sondern weil es kurz vorher live gegangen war und wir wissen wollten, was wir übersehen hatten.
Wir hatten einiges übersehen. Dieser Text nennt die Befunde beim Namen. Alles hier Beschriebene ist behoben, das meiste stand ohnehin bereits im öffentlichen Changelog.
Der Denkfehler, aus dem beide kritischen Befunde folgten
Wenn jemand bei uns Premium kauft, entsteht bei unserem Zahlungsdienstleister ein Kundendatensatz. Wir haben diesen Datensatz über die Server ID abgelegt, weil Premium ja auf einem Server landet.
Das ist genau das falsche Modell, und es war die Wurzel der beiden schwersten Befunde.
Ein Kundendatensatz gehört einem Käufer. Er trägt dessen Zahlungsmittel, dessen Rechnungshistorie und, sobald jemand für zwei Server zahlt, mehrere Abos. Ihn über die Server ID zu schlüsseln heißt: mehrere Menschen zeigen auf dieselbe Zeile.
Befund 1: Fremde Kundendaten überschreibbar, ohne zu zahlen
Der Kauf steht jedem Mitglied eines Servers offen, und die Funktion, die den Kundendatensatz holt oder anlegt, läuft vor der Zahlung. Sie fand die Zeile über die Server ID und schrieb sie stillschweigend auf den neuen Interessenten um, inklusive dessen E-Mail Adresse beim Zahlungsdienstleister.
Praktisch hieß das: Ein Klick auf einem Server, auf dem irgendwann jemand gekauft hatte, genügte. Danach wären die Rechnungen aller Abos dieses ursprünglichen Käufers an die E-Mail Adresse des Fremden gegangen. Ohne Zahlung, ohne Bestätigung.
Behoben, indem vor jeder Wiederverwendung geprüft wird, ob der Datensatz wirklich zu diesem Konto gehört. Bei Abweichung entsteht ein eigener, der fremde bleibt unangetastet.
Befund 2: Das Rechnungsportal zeigte fremde Abos
Derselbe Denkfehler, eine Ebene weiter. Die Berechtigungsprüfung galt dem Abo des Servers, die eröffnete Portal Sitzung aber dem Kundendatensatz, der ja per Design mehrere Server tragen kann.
Wer also auf einem Server legitim kaufte, konnte im Portal die Abos sehen und verwalten, die derselbe Datensatz für andere Server trug.
Behoben, indem der Kundendatensatz aus dem konkreten Kauf aufgelöst wird und nicht mehr aus der Serverzeile.
Zwei Befunde, bei denen der Code unseren eigenen AGB widersprach
Das ist die Kategorie, die uns am meisten gestört hat, weil sie nicht technisch ist.
Die Blacklist nahm bezahlte Zeit weg. In unseren AGB steht, dass ein Abo zum Ende der laufenden Periode endet. Beim Sperren eines Servers rief der Code aber die sofortige Kündigung: keine Erstattung, und ab sofort keine Leistung mehr für einen bereits bezahlten Zeitraum. Der Code hat also etwas anderes getan, als wir schriftlich zugesagt hatten. Jetzt läuft die bezahlte Periode aus.
Der Widerrufsverzicht war nicht beweisbar. Vor einem Kauf muss man bestätigen, dass die Leistung sofort beginnt. Diese Bestätigung lebte ausschließlich im Browser, wurde nie mitgeschickt und nirgends gespeichert. Die Beweislast liegt bei uns, und wir hatten schlicht keinen Beweis. Jetzt ist die Zustimmung Pflichtfeld der Kaufroute, wird mit Zeitstempel und versioniertem Wortlaut abgelegt, und der Wortlaut selbst ist versioniert: ein bestehender Text wird nie editiert, es entsteht immer eine neue Version, sonst wären alte Nachweise nicht mehr reproduzierbar.
Die zweite Runde war unangenehmer als die erste
Nach den ersten 21 Befunden haben wir ein paar Wochen später erneut nachgesehen. Das war die richtige Entscheidung, denn der schwerste Einzelbefund überhaupt kam erst dabei heraus.
Eine Erstattung auf Server B löschte die bezahlte Lifetime von Server A. Erstattungs- und Rückbuchungsmeldungen nennen nur eine Zahlung, keinen Server. Wir ordneten sie über den Kundendatensatz zu. Nachdem wir in Runde eins korrekt festgestellt hatten, dass ein Datensatz einem Käufer über mehrere Server gehört, war genau diese Zuordnung nicht mehr eindeutig. Wer zwei Server bezahlt hatte und für einen davon eine Erstattung bekam, verlor unter Umständen die Lifetime des anderen.
Jetzt läuft die Zuordnung über vier exakte Stufen, und was sich nicht eindeutig zuordnen lässt, wird gemeldet statt geraten.
Eine gewonnene Rückbuchung stellte nichts wieder her. Der Code löschte bei einer Rückbuchung die Kaufzeile, also den einzigen Nachweis, dass dieser Server je eine Lifetime hatte. Gewann man den Fall später, ließ sich das nicht rückgängig machen. Jetzt wird markiert statt gelöscht.
Die Datenaufbewahrung löschte bezahlte Server. Verlässt der Bot einen Server, werden nach 30 Tagen dessen Daten entfernt. Diese Bereinigung fragte nicht, ob dort ein laufendes Abo hängt.
Was wir daraus gelernt haben
Die Fehler lagen nicht im Bezahlcode. Kein einziger kritischer Befund betraf einen Betrag, eine Währung oder eine Berechnung. Sie lagen alle im Modell dahinter: wem gehört ein Kundendatensatz, wem gehört eine Zahlung, wem gehört ein Server. Wer ein Bezahlsystem prüft und auf Beträge schaut, schaut an der gefährlichen Stelle vorbei.
Ein Audit braucht eine zweite Runde. Der schwerste Befund war eine direkte Folge eines Fixes aus der ersten. Das ist kein Zufall: Wer ein Modell korrigiert, macht damit Annahmen ungültig, die anderswo noch gelten.
"Steht so in den AGB" ist ein prüfbarer Satz. Zwei Befunde waren Abweichungen zwischen dem, was wir schriftlich zugesagt hatten, und dem, was der Code tat. Das lässt sich lesen, und niemand tut es, weil es nach Jura aussieht und nicht nach Technik.
Die Kaufbedingungen im Wortlaut stehen in unseren AGB, das Widerrufsrecht unter Widerruf.
Weitere Artikel
Warum bei AlphaCloud fast alles kostenlos bleibt
Die meisten Discord Bots sperren ihre nützlichsten Funktionen hinter ein Abo. Wir machen das bewusst nicht, und das hat weniger mit Großzügigkeit zu tun, als es klingt.
Hinter den KulissenWelche Gesetze in unserem Code stecken
Ein Kündigungsknopf, der ohne Login erreichbar sein muss. Ein Formular, das niemals ablehnen darf. Warum Rechtspflichten bei uns Architektur sind und keine Textbausteine.
Hinter den KulissenWie wir 4.000 Discord Server im Blick behalten: Grafana, PostHog und was sie uns gezeigt haben
Ein Loop, der still die Hälfte unseres Verkehrs erzeugte, drei Millionen unnötige Anfragen am Tag und ein Angriff, der nach einer Minute vorbei war. Was unser Monitoring sieht, und was es bewusst nicht sieht.