Webhooks
Ein Webhook ist eine HTTP-Anfrage, die Invoise an Ihren Server sendet, wenn sich eine Zahlung ändert. Sie können mit GET-Statusprüfungen beginnen und Webhooks hinzufügen, wenn Sie automatische Updates benötigen.
1. Ihren Endpunkt registrieren
Abschnitt betitelt „1. Ihren Endpunkt registrieren“curl -X POST 'https://platform.invoise.me/api/v1/shops/{shop_id}/webhooks' \ -H 'Authorization: Bearer ivk_...' \ -H 'Idempotency-Key: <saved-unique-key>' \ -H 'Content-Type: application/json' \ -d '{"url":"https://example.com/invoise","filters":["transfer","invoice","payout"]}'Verwenden Sie Ihre eigene öffentliche HTTPS-URL. Lokale und private Adressen werden abgelehnt. Speichern Sie das beim Erstellen des Endpunkts zurückgegebene secret: Sie benötigen es, um Anfragen zu verifizieren.
Ein Shop kann bis zu 10 Endpunkte haben. Die URL eines Endpunkts kann geändert werden, verwenden Sie also einen weiter, statt einen weiteren hinzuzufügen. Siehe Limits.
2. Die benötigten Ereignisse auswählen
Abschnitt betitelt „2. Die benötigten Ereignisse auswählen“| Ereignis | Bedeutung |
|---|---|
transfer.observed |
Ein Transfer wurde gesehen; er ist noch nicht bestätigt. |
transfer.confirmed |
Ein eingehender Transfer hat genügend Bestätigungen. Es kann nur eine teilweise Rechnungszahlung sein. |
transfer.reverted |
Ein vorheriges Transfer-Ereignis wurde zurückgenommen. |
invoice.closed |
Die Rechnung wurde on-chain abgeschlossen. |
invoice.cancelled |
Die Zahlungsseite wurde storniert oder die Rechnung ist abgelaufen; dies ist keine Rückerstattung. |
payout.sent |
Die Auszahlung wurde gesendet oder ihr ausgehender Transfer wurde gesehen. |
payout.confirmed |
Eine vollständige Abrechnungsbuchung ist verfügbar. |
payout.reverted |
Eine vorherige Auszahlungsbestätigung wurde zurückgenommen. |
payout.failed |
Ein endgültiger Auszahlungsfehler erfordert ein Eingreifen. |
Bei invoice.cancelled gibt data.reason den Grund an: expired, wenn die Rechnungslaufzeit abgelaufen ist, merchant_blocked, wenn das Invoise-Team den Händler gesperrt hat. Eine Stornierung durch den Händler hat keinen reason.
Abonnieren Sie nach Gruppe: transfer, invoice, payout. Eingehende Transfers und ausgehende Auszahlungen sind unterschiedliche Ereignisse. Eine vorübergehende RPC- oder Gas-Verzögerung ist kein payout.failed.
gas.wait und sweep.failed werden als Filter akzeptiert, aber Invoise sendet sie nicht an Ihren Endpunkt. Es sind interne Signale: gas.wait bedeutet, dass eine Auszahlung auf Mittel für die Netzwerkgebühr wartet und von selbst fortfährt; sweep.failed bedeutet, dass ein Versuch, die Mittel zu Ihrem Empfänger zu bewegen, fehlgeschlagen ist. Für keines von beidem ist eine Aktion Ihrerseits nötig. Eine Auszahlung, die nicht abgeschlossen werden kann, erreicht Sie als payout.failed.
In einem Sandbox-Shop können Sie beides über POST /shops/{shop_id}/sandbox/simulate simulieren, um zu prüfen, dass eine verzögerte Auszahlung Ihren Ablauf nicht stört. Sie ändern nur den Zustand der Auszahlung und senden keinen Webhook.
3. Vor der Verarbeitung überprüfen
Abschnitt betitelt „3. Vor der Verarbeitung überprüfen“Invoise-Signature hat die Form t=<timestamp>,v1=<hex>. Die Signatur ist HMAC-SHA256 von timestamp + "." + raw_body, unter Verwendung Ihres Webhook-Secrets.
Das folgende Node.js-Beispiel lehnt außerdem Zeitstempel ab, die mehr als fünf Minuten von der Serveruhr abweichen. Halten Sie diese Uhr synchronisiert und wählen Sie Ihre Toleranz bewusst.
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verify(rawBody, header, secret) { const match = /^t=(\d+),v1=([a-f0-9]{64})$/.exec(header ?? ''); if (!match) return false; const [, timestamp, signature] = match; const age = Math.abs(Date.now() / 1000 - Number(timestamp)); if (!Number.isFinite(age) || age > 300) return false; const expected = createHmac('sha256', secret) .update(timestamp + '.') .update(rawBody) .digest(); return timingSafeEqual(Buffer.from(signature, 'hex'), expected);}Übergeben Sie die ursprünglichen Anfrage-Bytes als rawBody. Parsen Sie das JSON nicht und wandeln Sie es nicht wieder in einen String um, bevor Sie die Signatur prüfen. Lehnen Sie ungültige Signaturen ab, bevor Sie ihren Inhalt speichern oder darauf reagieren.
4. Einmal verarbeiten
Abschnitt betitelt „4. Einmal verarbeiten“Eine gekürzte Payload sieht so aus:
{ "id": "<event-id>", "type": "transfer.confirmed", "created_at": "2026-09-19T10:00:00Z", "data": { "issuance_id": "<issuance-id>", "shop_id": "<shop-id>", "external_id": "order-123", "type": "invoice", "invoice_cancelled": false }}| Fall | Behandlung |
|---|---|
| Duplikate | Deduplizieren Sie anhand von Invoise-Event-ID, die der id der Envelope entspricht. Speichern Sie die ID und das Geschäfts-Update atomar. |
| Ein Ereignis annehmen | Speichern Sie angenommene Ereignisse dauerhaft, geben Sie umgehend 2xx zurück und verarbeiten Sie anschließend die Warteschlange. |
| Wiederholungen und Replays | Wiederholungen und manuelle Replays verwenden dieselbe Ereignis-ID erneut. Invoise-Attempt-ID ändert sich und ist kein Deduplizierungsschlüssel. |
| Ereignisreihenfolge | Ereignisse können in falscher Reihenfolge eintreffen. Verwenden Sie deren Inhalt und gleichen Sie bei Bedarf mit dem aktuellen Status ab. |
Bei Rückabwicklungen verweist chain_event.reference_event_id auf das frühere Chain-Ereignis. Auszahlungs-Payloads enthalten eine stabile payout_id, einen Transaktions-Hash, Beträge und Gebührendetails; unbekannte Beträge sind null. Ihre Liste eingehender Transfer-IDs ist auf 100 begrenzt und markiert eine Kürzung.
Ältere gespeicherte Ereignisse können project_id verwenden; lesen Sie zuerst shop_id und greifen Sie bei Bedarf darauf zurück. Replays behalten die ursprünglichen Bytes.
Fehlgeschlagene Zustellung prüfen
Abschnitt betitelt „Fehlgeschlagene Zustellung prüfen“Listen Sie mit GET /api/v1/shops/{shop_id}/deliveries auf. Wiederholen Sie eine Zustellung mit POST /api/v1/shops/{shop_id}/deliveries/{id}/replay und einem gespeicherten Idempotenzschlüssel. Das Wiederholen erstellt kein neues Geschäftsereignis.