Backend
Idempotenz: das unscheinbarste Feature einer Zahlungs-API
Netzwerke sind unzuverlässig, Clients wiederholen Anfragen. Warum ein Idempotency-Key kein Detail ist, sondern die Grundlage.
Eine Anfrage geht raus, die Antwort kommt nicht zurück. Timeout. Der Client versucht es noch einmal. Hat die Zahlung jetzt einmal stattgefunden – oder zweimal? Wenn eine API diese Frage nicht eindeutig beantworten kann, ist sie für Geld nicht geeignet.
Das Prinzip
Ein Endpoint ist idempotent, wenn dieselbe Anfrage – beliebig oft wiederholt – denselben Effekt hat wie einmal. Für GET ist das selbstverständlich. Für POST /payments muss man es bauen.
Der Client schickt mit jeder schreibenden Anfrage einen selbst erzeugten Schlüssel:
POST /v1/payments
Idempotency-Key: 8f1c2a4e-3b7d-4c9e-a1f0-5d6e7f8a9b0c
Der Server merkt sich unter diesem Schlüssel das Ergebnis der ersten Verarbeitung. Jede Wiederholung mit demselben Schlüssel bekommt dieselbe Antwort – ohne die Zahlung erneut auszuführen.
Was oft übersehen wird
Der Schlüssel muss zur Anfrage passen. Derselbe Schlüssel mit einem anderen Body ist ein Fehler, keine Wiederholung. Wir prüfen einen Hash des Bodys und antworten mit 422, wenn er abweicht.
Nebenläufige Wiederholungen. Zwei identische Anfragen treffen im selben Moment ein. Der Schlüssel muss vor der Verarbeitung reserviert werden – ein INSERT ... ON CONFLICT DO NOTHING in derselben Transaktion – sonst arbeiten beide.
Ablauf. Schlüssel leben nicht ewig. 24 Stunden sind ein guter Standard; danach ist eine Wiederholung eine neue Anfrage.
Ein Ausschnitt in Go
func (s *Service) CreatePayment(ctx context.Context, key string, req PaymentRequest) (Payment, error) {
return s.idem.Run(ctx, key, req.Hash(), func(ctx context.Context) (Payment, error) {
return s.payments.Create(ctx, req)
})
}
Die gesamte Logik – reservieren, ausführen, Ergebnis ablegen, bei Wiederholung zurückgeben – lebt in einem Paket, das jeder schreibende Endpoint benutzt. Kein Endpoint entscheidet selbst, ob er idempotent ist.
Warum das die Grundlage ist
Retries sind kein Fehlerfall, sie sind der Normalfall: mobile Netze, Load Balancer, Timeouts, Queues mit At-least-once-Zustellung. Eine API, die Wiederholungen nicht als Wiederholungen erkennt, verlagert das Problem in die Buchhaltung des Kunden. Dort ist es teurer.