Webhooks: Ereignisse, Signaturen und Zustellung
Registrieren Sie einen öffentlichen HTTPS-Endpunkt, weisen Sie die Kontrolle nach und empfangen Sie signierte Rechnungs- und Zahlungsereignisse. Die Zustellung wird dauerhaft in die Warteschlange gestellt; die erneute Verarbeitung desselben Ereignisses muss für Ihre Bestellung sicher sein.
Auf dieser Seite
Woher Ereignisse kommen#
invoice.created bedeutet nicht bezahlt. Zahlungsereignisse stammen aus einer verifizierten Serveroperation nach einem aktuellen Gateway-Lesezugriff; ein eingehender Ereignisname allein ist unzureichend.
Gateway → Kevmi-Eingang und Kevmi → Händler-Zustellung sind verschiedene Abschnitte mit separaten Geheimnissen. Der Browser, Rücknavigation und Widget-Nachrichten setzen keinen Finanzstatus.
| Ereignis | Quelle |
|---|---|
| invoice.created | Rechnung gespeichert |
| invoice.updated | Link-Verfügbarkeit geändert |
| payment.succeeded | Exakte Zahlung vom Server verifiziert |
| payment.failed | Fehlgeschlagene Operation verifiziert |
| payment.refunded | Vollständige Rückerstattung verifiziert |
| payment.partially_refunded | Teilweise Rückerstattung verifiziert |
| payment.disputed | Streitfall verifiziert |
| payment.review_required | Operation erfordert zusätzliche Prüfung |
Registrieren und verifizieren Sie einen Endpunkt#
URLs können nicht auf IP-Adressen, localhost, private Netzwerke, nicht standardmäßige Ports zielen oder Anmeldedaten enthalten. DNS wird vor der Zustellung geprüft; ausgehende POST-Anfragen folgen keinen Weiterleitungen. Ein Konto kann bis zu fünf Endpunkte registrieren.
Das Verifizierungsereignis hat livemode=false. Ein erfolgreicher HTTP-Status allein ist unzureichend: Die Herausforderung muss exakt übereinstimmen. Das Rotieren des Geheimnisses erfordert eine erneute Verifizierung.
- 1
Unter /account#webhooks in Ihrem Händlerkonto geben Sie eine öffentliche HTTPS-URL, eine Beschreibung und die benötigten Ereignistypen ein.
- 2
Speichern Sie das ausgestellte Geheimnis im serverseitigen Speicher. Sein voller Wert wird nur einmal angezeigt.
- 3
Verifizieren Sie Signaturen über die ursprünglichen Anfragebytes.
- 4
Führen Sie die Endpunktverifizierung aus. Für endpoint.verification geben Sie JSON zurück, das data.challenge wiedergibt: {"challenge":"the received value"}.
- 5
Aktivieren Sie den verifizierten Endpunkt und prüfen Sie den Zustellungsverlauf.
Ereignisumschlag#
id bleibt bei jedem Zustellversuch eines Ereignisses gleich. createdAt ist ein Unix-Zeitstempel in Millisekunden und apiVersion identifiziert die Vertragsversion. data enthält das gespeicherte Serverobjekt; das Beispiel ist gekürzt.
livemode=true unterscheidet ein echtes Serverereignis von der Endpunktverifizierung. Es bedeutet nicht, dass eine Rechnung bezahlt ist: Prüfen Sie den Typ und die Zahlungsdaten. Behandeln Sie bekannte Ereignistypen und validieren Sie das Schema.
{
"id": "evt_example",
"type": "invoice.created",
"apiVersion": "2026-10-21",
"createdAt": 1792540800000,
"livemode": true,
"data": {
"invoice": {
"id": "lnk_example",
"orderId": "order-1042",
"currency": "USD",
"amountMinor": 500,
"vatCustomerPercent": 100,
"feeCustomerPercent": 0
}
}
}HMAC-SHA256 verifizieren#
Kevmi-Signature enthält t=seconds,v1=hex. Die Signatur ist HMAC-SHA256 über die Zeitstempelzeichenfolge, einen Punkt und den ursprünglichen Text: timestamp + "." + rawBody. Verwenden Sie das spezifische Endpunktgeheimnis und einen Vergleich in konstanter Zeit.
Lehnen Sie Signaturen ab, deren Zeitstempel um mehr als 300 Sekunden in beide Richtungen von der Serverzeit abweicht. Synchronisieren Sie die Uhren. Parsen und serialisieren Sie JSON vor der Verifizierung nicht neu: Leerzeichen und Eigenschaftsreihenfolge beeinflussen die signierten Bytes.
import { createHmac, timingSafeEqual } from 'node:crypto';
function verifyKevmi(rawBody, header, secret, nowSeconds) {
const match = /^t=(\d{10}),v1=([a-f0-9]{64})$/.exec(header || '');
if (!match || Math.abs(nowSeconds - Number(match[1])) > 300) {
return false;
}
const expected = createHmac('sha256', secret)
.update(match[1] + '.')
.update(rawBody)
.digest();
const received = Buffer.from(match[2], 'hex');
return expected.length === received.length
&& timingSafeEqual(expected, received);
}
// Read bytes before JSON parsing; cap the request body.
// secret comes from your server secret store.
const valid = verifyKevmi(rawBody, signature, secret,
Math.floor(Date.now() / 1000));
if (!valid) throw new Error('Invalid webhook signature');
const event = JSON.parse(rawBody.toString('utf8'));
// Validate the event schema and persist its ID together
// with your order update in one database transaction.
// Return a 2xx response after durable acceptance.Wiederholungsversuche und Bestätigung#
Jede 2xx-Antwort bestätigt ein normales Ereignis. Speichern Sie das Ereignis dauerhaft, bevor Sie antworten; aufwendige Arbeit kann über Ihre eigene Warteschlange fortgesetzt werden. Das Antwortzeitlimit beträgt acht Sekunden. Ein Netzwerkfehler oder eine Nicht-2xx-Antwort plant einen Wiederholungsversuch ein.
Es gibt höchstens sechs Versuche: die ursprüngliche Zustellung plus Verzögerungen von einer Minute, fünf Minuten, 30 Minuten, zwei Stunden und 12 Stunden nach aufeinanderfolgenden Fehlern. Dies sind Warteschlangenpläne, keine Garantien für die genaue Zustellzeit. Das Ausschöpfen der Versuche markiert die Zustellung als fehlgeschlagen.
Die Zustellung erfolgt mindestens einmal und verspricht keine strenge Reihenfolge. Speichern Sie die eindeutige Ereignis-ID in derselben Transaktion wie Ihre Bestellaktualisierung. Ein bereits angewendetes Duplikat sollte 2xx erhalten, ohne die Bestellung erneut auszuführen.
Ein manueller Wiederholungsversuch startet die Zustellversuche neu, wobei die ursprüngliche Ereignis-ID, Zustell-ID und der genaue Text erhalten bleiben. Jeder Versuch erhält einen neuen Signaturzeitstempel. Das Pausieren eines Endpunkts erhält seine Warteschlange; das Löschen bricht ausstehende und fehlgeschlagene Zustellungen ab.
Zustellhistorie und Abgleich#
Die Oberfläche zeigt den Endpunktstatus, letzte Zustellungen, Versuchszahlen, letzte HTTP-Antwort, Fehler und den Zeitpunkt des nächsten Versuchs. Diese Diagnosen ergänzen Ihre eigenen Bestellaufzeichnungen.
GET /api/v1/events mit events:read liefert bis zu 50 Ereignisse und nextCursor. Übergeben Sie den undurchsichtigen nextCursor unverändert als cursor; null markiert das Ende. Die Sortierung verwendet sowohl createdAt als auch id. Das alte Unix-Millisekunden-Grenzwertfeld bleibt separat verfügbar und kann nicht mit cursor kombiniert werden. Verifizierte Endpunkte erhalten abonnierte Ereignisse nicht früher als ihre Registrierung; pausierte Endpunkte sammeln weiterhin in der Warteschlange stehende Ereignisse für die Zustellung nach der Wiederaufnahme. Dies verspricht keine Historie vor der Endpunkterstellung.
Eingehende Gateway-Quittungsprüfungen#
Gateway v3 postet an /api/gateway/webhook mit X-Gateway-Event-Id, X-Gateway-Timestamp und X-Gateway-Signature: v1=<hex>. HMAC-SHA256 signiert timestamp + "." + rawBody mit GATEWAY_WEBHOOK_SECRET; das Uhrenfenster beträgt 300 Sekunden. Die Übereinstimmung von ID im Header/Text, API-Version, Live-Modus und Text-Hash wird geprüft.
Eine Ereignis-ID, die mit unterschiedlichen Textbytes wiederverwendet wird, wird abgelehnt. Eine signierte Benachrichtigung löst ein authentifiziertes GET des genauen Vorgangs aus. Bestellung, Methode, Währung, Basis, genehmigter Gesamtbetrag und Version werden geprüft. Der Erfolg erfordert fulfillment_ready=true, verification=provider_confirmed, den genauen bezahlten Betrag und keinen Streitfall; Krypto benötigt außerdem bestätigte Abrechnungsnachweise.
Ingress-Behandlung, Quittungen und Händlerereignisse werden vor Duplikaten geschützt. Alte reine Browser-Benachrichtigungsformate können keine erfolgreiche Zahlung feststellen. Händler-Webhooks enthalten niemals Kartenfelder, Bankcodes, QR-Daten, Anbieterzugangsdaten oder das Gatewaygeheimnis.
Telegram hat einen separaten Vertrag#
POST /api/telegram/webhook verifiziert X-Telegram-Bot-Api-Secret-Token, die Update-Struktur und den Absender. update_id schützt vor doppelter Verarbeitung. Diese Route behandelt Anmeldung, Befehle, Support und Moderationsaktionen; sie akzeptiert keine Finanzquittungen.
Der Bereitstellungseigentümer konfiguriert das Bot-Token, das Webhook-Geheimnis, die setWebhook-Registrierung und eine erreichbare HTTPS-Adresse. Sie sind getrennt vom Signaturgeheimnis des Händler-Endpunkts.
Mehrere Geschäfte und ein USD-Basispreis#
Wählen Sie Alle Geschäfte für aggregierte Analysen und Betrieb, oder wählen Sie ein Geschäft für dessen eigene Aufzeichnungen. Jedes Geschäft hat eine separate storeId, einen Namen, einen MwSt-Standardwert, Rechnungen, API-Schlüssel, Webhook-Endpunkte, Ereignisverlauf und Website-Anwendung. Benachrichtigungen und Support-Tickets gehören zum Konto. Ein Konto kann bis zu 100 Geschäfte erstellen. Vorhandene Datensätze verbleiben im ersten Geschäft.
Neue Workspace-Rechnungen verwenden pricingMode="universal" und einen USD-Basispreis. Any umfasst Karte, Alipay, SBP und Krypto; ein Händler kann eine Teilmenge auswählen. Karten und Alipay berechnen USD. SBP berechnet RUB unter Verwendung des USD/RUB-Referenzkurses der Bank von Russland. Krypto berechnet USDT gemäß der expliziten Rechnungsregel 1 USD = 1 USDT. Dies ist eine Rechnungsumrechnungsregel, kein Markt-USDT-Kurs. Das Gateway liefert den Kurs der ausgewählten Münze, den genauen Überweisungsbetrag und Anweisungen vor der Überweisung.
Der Referenzkurs und beide Beträge werden bei der Erstellung eingefroren. Rechnungen mit RUB-Umrechnung verfallen innerhalb von 24 Stunden. Wenn der Referenzkurs nicht abgerufen werden kann, schlägt die Erstellung mit FX_UNAVAILABLE fehl; es wird kein erfundener oder veralteter Ersatzkurs verwendet. Der Basisbetrag Minor und Angebote verwenden die USD-Buchungswährung. quotes[method].settlement enthält das native Belastungsangebot. Der öffentliche Checkout legt die ausgewählte Belastungswährung in currency, den ursprünglichen Preis in baseAmountMinor/baseCurrency und den Referenzkurs in fx offen. Sobald eine Zahlung ausgelöst wurde, kann ihre Methode nicht geändert werden; eine Rechnung hat einen Gateway-Vorgang.
Erstellen Sie einen API-Schlüssel im vorgesehenen Geschäft und senden Sie Authorization: Bearer <key> von Ihrem Server. Seine Geschäftsbindung ist unveränderlich; X-Kevmi-Store kann ihm keinen Zugriff auf ein anderes Geschäft gewähren. Browser-Sitzungen wählen X-Kevmi-Store: <storeId>; wenn es weggelassen wird, wird das erste Geschäft ausgewählt. Der Wert all wird nur von GET /api/account/dashboard und GET /api/payment-links akzeptiert. Mutationen erfordern ein ausgewähltes Geschäft, Origin und X-CSRF-Token. Konto-IDs und Geschäfts-IDs sind unterschiedliche Kennungen.
Aus Kompatibilitätsgründen behalten API-Anfragen ohne pricingMode="universal" native Preise und die ursprünglichen Währungsbeschränkungen bei. Bestehende Rechnungen werden nie neu berechnet. Bestätigte Zahlungseingänge in Analysen spiegeln verifizierte Zahlungen abzüglich gespeicherter Rechnungsausgaben wider; sie sind kein abhebbarer Saldo. Legacy-Rechnungswährungen bleiben getrennt, und Auszahlungen werden mit dem Support vereinbart. Führen Sie eine Bestellung erst aus, nachdem Sie payment.fulfillmentReady=true und payment.status="succeeded" über die Server-API, den Ereignisverlauf oder einen signierten Webhook geprüft haben.