Arbeitsprobe für Klaus Calonder
Jede Zahlung genau einmal verbucht.
Ein Stripe-Webhook-Empfänger nach der Stripe-Doku, gebaut mit Claude Code, von einem zweiten Agenten gegen die Doku geprüft und mit 26 Tests abgesichert. Probieren Sie es aus.
Zahlungen Beispieldaten
| Vorgang | Betrag | Status | Buchungen |
|---|
Noch keine Zahlung verbucht.
Zustellungen neueste oben
Hier läuft derselbe Prüf- und Buchungscode wie im Projekt (stripe-signature.js, intake.js), mit einer Ablage im Arbeitsspeicher statt SQLite. Die Verwaltungsseite des Servers zeigt dieselben Daten als schlichte Tabelle. Jede Zustellung wird beim Laden der Seite frisch signiert und geprüft.
Schicken Sie selbst eine Zustellung.
Selbst signierte Testereignisse, gebildet wie bei Stripe: Zeitstempel, Punkt, Inhalt, HMAC-SHA256.
Was dahinter steckt
Drei Sätze aus Ihrer Anzeige, im Code beantwortet.
-
„Zahlungs- und E-Commerce-Funktionen einbinden, beispielsweise mit Stripe“
Gebaut nach der Stripe-Doku, Schritt für Schritt.
Signatur über Zeitstempel und Originalbytes, Vergleich in konstanter Zeit, fünf Minuten Toleranz gegen abgefangene Nachrichten, mehrere gültige Geheimnisse während eines Wechsels. Eine abgeschlossene Checkout-Sitzung gilt erst als bezahlt, wenn Stripe die Zahlung meldet, und kommt eine Zahlung vor ihrer Rechnung an, bleibt sie trotzdem bezahlt.
-
„die Qualität, Sicherheit und Wartbarkeit der entwickelten Software sicherzustellen“
26 Tests, 15 davon gegen den laufenden Server.
Fälschung, veränderter Betrag, alte Zustellung, fünf gleichzeitige Wiederholungen derselben Zahlung, vertauschte Reihenfolge, verzögerte Zahlungsart, Größenlimit, geschützte Verwaltung. Ein zweiter Agent hat den Code zusätzlich gegen die Stripe-Doku, auf Sicherheit und auf Wartbarkeit geprüft, alle 15 Funde sind eingearbeitet.
-
„sauber dokumentiert, langfristig wartbar und unabhängig von einzelnen Personen weitergeführt“
Jeder Teil getrennt, jeder Teil getestet.
Signaturprüfung, Entscheidung, Ablage und Server sind getrennte ES-Module. Die Signaturprüfung ist hier selbst geschrieben, damit sie in Ihrem Browser mitläuft; im Livebetrieb kommt an diese eine Stelle die offizielle Stripe-Bibliothek, die Stripe selbst empfiehlt. Ein Vertragstest schickt alle Fälle durch jede Ablage, so lässt sich SQLite später gegen eine größere Datenbank tauschen, ohne die Entscheidungslogik anzufassen. Das README ordnet die Vorgaben der Stripe-Doku den Stellen im Code zu.
// Schritte 2 und 3: signed_payload als Bytes, HMAC-SHA256.const message = signedPayload(timestamp, body);let match = false;for (const secret of keys) { const expected = await hmacHex(secret, message); for (const received of signatures) { // Schritt 4a: jede Signatur gegen jedes Geheimnis. if (timingSafeEqualHex(expected, received)) match = true; }}if (!match) return { ok: false, reason: 'signature_mismatch' };// Schritt 4b: Alter gegen die Toleranz (gegen Wiederholung).const age = nowSeconds - Number(timestamp);if (Math.abs(age) > tolerance) { return { ok: false, reason: 'timestamp_outside_tolerance', age };}
✔ fünf gleichzeitige Zustellungen desselben Ereignisses ergeben eine Buchung
✔ Checkout ohne sofortige Zahlung: erst „ausstehend“, nach async_payment_succeeded „bezahlt“
✔ ein veränderter Betrag im Inhalt wird abgelehnt
…
ℹ tests 26 · pass 26 · fail 0
Mit README: starten, testen, was vor dem Livegang noch zu tun ist.
So arbeite ich mit Claude Code
Schnell gebaut, erst fertig, wenn es geprüft ist.
Diese Arbeitsprobe ist am 10. November 2026 entstanden. Die Uhrzeiten stammen aus den Dateien und Testläufen.
- 13:27
Erste Datei
Signaturprüfung nach den vier Schritten der Stripe-Doku.
- 13:29
Erste Fassung, 20 Tests grün
Selbst signierte Ereignisse, echter Server, echte SQLite-Datei.
- 13:38
Zweiter Agent prüft
15 Befunde zu Stripe-Doku, Sicherheit und Wartbarkeit. Der wichtigste: Eine abgeschlossene Checkout-Sitzung galt immer als bezahlt.
- 13:41
Nachgebessert, 25 von 25 grün
Alle Befunde bis auf zwei Stilfragen eingearbeitet, fünf Tests dazu.
- 14:13
Zweite Prüfrunde umgesetzt
Zwei weitere Prüfer-Agenten lasen Seite und Code. Danach ES-Module, englische Bezeichner und ein Test mehr: 26 von 26 grün, auch unter Node 22.
Seit August praktisch täglich
Claude Code ist mein tägliches Werkzeug, in Kundenprojekten wie in eigenen.
Ein Agent baut, ein zweiter prüft
Seit Oktober prüfen in der Regel eigene Prüfer-Agenten, was ein Bau-Agent geliefert hat, jeder mit eigenem Prüfauftrag.
Zu jeder Lieferung ein Nachweis
Was gemessen, getestet und geliefert wurde, steht in einer Datei, die man später nachlesen kann.
Eigene Regeln für die KI
Ein gemeinsames Gedächtnis mit einer Notiz je Kunde und ein selbst geschriebener Skill für wiederkehrende Recherche.
Vorschlag
So würde ich bei Ihnen starten.
- Woche 1
Bestand aufnehmen
Jede Ihrer Webanwendungen: Technikstand, Ladezeit am Handy, Sicherheit, technisches SEO, Hosting und Sicherungen. Ergebnis als kurzer Befund mit Reihenfolge.
- Woche 2
Fundament legen
Alles in Git, Zugänge und Geheimnisse sauber getrennt, automatische Sicherungen geprüft, Tests für das Wichtigste.
- Woche 3
Erste Modernisierung live
Der Punkt mit dem größten Nutzen aus Woche 1, in kleinen Schritten, jede Änderung nachvollziehbar.
- Woche 4
Nächste Ideen technisch planen
Gemeinsam die nächsten Projekte durchgehen und je Idee einen schlanken ersten Bauabschnitt festlegen.
Zuletzt bei einem Hersteller aus der Produktfotografie: Die Startseite lud am Handy nach meiner Arbeit 1,0 statt 7,4 MB (eigene Messung vorher und nachher, 21. und 25.09.2026), und 375 alte Adressen führen in einem Schritt zur richtigen Seite.
Ein Vorschlag ohne Kenntnis Ihrer Systeme. Die Reihenfolge legen Sie fest.
Alle Belege ansehen
Was diese Arbeitsprobe ist und was nicht
Ein lauffähiges Node.js-Projekt aus ES-Modulen (Node 22.13 oder neuer), getestet mit Node 24.15 und 22.23 unter Windows. Kein Stripe-Konto, keine echten Schlüssel, keine echte Zahlung: Alle Ereignisse sind Beispieldaten und werden mit einem zufälligen Testgeheimnis signiert. Vor einem echten Einsatz gehören die offizielle Stripe-Bibliothek, ein Endpunkt im Stripe-Dashboard und ein Gegentest mit Ereignissen aus dem Stripe-Testmodus dazu (steht im README).
Vorgaben aus der Stripe-Doku (docs.stripe.com/webhooks und /checkout/fulfillment, gelesen am 10.11.2026)
„We recommend using our official libraries to verify signatures.“
„To protect against timing attacks, use a constant-time-string comparison to compare the expected signature to each of the received signatures.“
„Our libraries have a default tolerance of 5 minutes between the timestamp and the current time.“ · „Don’t use a tolerance value of 0. Using a tolerance value of 0 disables the recency check entirely.“
„Stripe requires the raw body of the request to perform signature verification.“
„Webhook endpoints might occasionally receive the same event more than once. You can guard against duplicated event receipts by logging the event IDs you’ve processed, and then not processing already-logged events.“
„Stripe doesn’t guarantee the delivery of events in the order that they’re generated.“
„Zahlungsmethoden mit Verzögerung generieren ein checkout.session.async_payment_succeeded-Ereignis, wenn die Zahlung später erfolgreich durchgeführt wird.“ (deutsche Fassung der Checkout-Doku)
Die 26 Tests (Lauf 10.11.2026, 14:13, Node 24.15 und 22.23)
- Zahlung kommt an und wird genau einmal verbucht
- Stripe stellt dasselbe Ereignis erneut zu: 200, aber keine zweite Buchung
- fünf gleichzeitige Zustellungen desselben Ereignisses ergeben eine Buchung
- zweites Ereignis-Objekt zum selben Vorgang (neue ID, gleiches Objekt und Typ) wird erkannt
- Reihenfolge vertauscht: „bezahlt“ vor „erstellt“, die Rechnung bleibt bezahlt
- gefälschte Zustellung: 400, nichts gespeichert
- abgefangene alte Zustellung (älter als 5 Minuten): 400, nichts gespeichert
- nicht abonnierter Ereignistyp: 200, festgehalten, keine Wirkung
- Schutz der Schnittstelle: GET 405, zu großer Inhalt sofort 413, unbekannter Pfad 404
- Verwaltung: ohne Passwort 401, mit Passwort 200 und die Zahlung ist zu sehen
- Migrationen: neue Datenbank auf aktuellem Stand, erneutes Öffnen ändert nichts
- Checkout ohne sofortige Zahlung: erst „ausstehend“, nach async_payment_succeeded „bezahlt“
- Checkout ohne Zahlungspflicht (z. B. 100-%-Gutschein): „ohne Zahlung“, nicht „ausstehend“
- Checkout mit Rechnung plus invoice.paid: eine Zahlung, eine Zeile
- nicht abonnierter Typ ohne Objekt-ID (z. B. balance.available): 200, kein 400
- gültige Signatur wird angenommen (Schritte 1 bis 4 der Stripe-Doku)
- Signatur stimmt mit der Node-eigenen HMAC-Berechnung überein
- falsches Geheimnis wird abgelehnt
- ein veränderter Betrag im Inhalt wird abgelehnt
- neu formatiertes JSON wird abgelehnt (darum Rohtext statt geparstem Objekt)
- Zeitstempel: 300 Sekunden alt geht, 301 nicht (Wiederholungsangriff)
- Toleranz 0 oder unendlich wird verweigert (würde die Altersprüfung abschalten)
- geprüft werden die Originalbytes, auch wenn sie kein gültiges UTF-8 sind
- fehlende oder kaputte Kopfzeile wird abgelehnt, v0 zählt nicht
- Geheimnis-Wechsel: zwei Signaturen in einer Kopfzeile, jedes der beiden Geheimnisse passt
- Speicher-Ablage und SQLite-Ablage entscheiden identisch
Probelauf als echter Server (10.11.2026, 14:14, Antworten gekürzt)
erste Zustellung: 200 {"outcome":"booked","status":"paid"}
Wiederholung durch Stripe: 200 {"outcome":"duplicate","reason":"event_id_seen"}
gefälscht: 400 {"outcome":"rejected","reason":"signature_mismatch"}
Verwaltung: 200, zeigt CHF 49.00 bezahlt
Prüfung durch einen zweiten Agenten
Ein Unter-Agent mit eigenem Prüfauftrag (Stripe-Doku, Sicherheit, README, Wartbarkeit) hat den Code von 13:31 bis 13:38 geprüft und eigene Versuche dagegen laufen lassen. Ergebnis: 1 wichtiger Befund, 5 sollten behoben werden, 9 Kleinigkeiten. Bestätigt hat er unter anderem die Signaturschritte 1 bis 4, den Vergleich in konstanter Zeit, die Toleranz, die Idempotenz in der Transaktion und die Maskierung der Verwaltung. Alle 15 Befunde sind eingearbeitet:
- Wichtig: Eine abgeschlossene Checkout-Sitzung galt immer als bezahlt, auch bei verzögerten Zahlungsarten. Jetzt wird
payment_statusausgewertet, dazuasync_payment_succeededundasync_payment_failed. - Nicht abonnierte Ereignisse ohne Objekt-ID bekamen 400, Stripe hätte sie tagelang wiederholt. Jetzt 200.
- Checkout mit Rechnung plus
invoice.paidergab zwei Zeilen für eine Zahlung. Jetzt eine. - Die Ablage der Vorführung war ungetestet. Jetzt schickt ein Vertragstest dieselben Fälle durch beide Ablagen.
- Das README nennt jetzt HTTPS und NTP als Pflicht vor dem Livegang.
- Dazu: Prüfung über die Originalbytes, sofortiges 413 bei zu großen Inhalten, Wartezeit bei gesperrter Datenbank, Toleranz „unendlich“ gesperrt, Beträge je Währung richtig formatiert, abgewiesene Zustellungen im Protokoll, geordnetes Herunterfahren,
.gitignore, ES-Module und englische Bezeichner.
Danach haben zwei weitere Prüfer-Agenten Seite und Code gelesen. Daraus kamen unter anderem der Test für einen veränderten Betrag, die Regel für Sitzungen ohne Zahlungspflicht und der Hinweis auf die offizielle Stripe-Bibliothek im README.