CommunityCodierung & Entwicklunggithub.com

Nomba Webhooks

Erstelle einen sicheren Nomba-Webhook-Endpunkt, der Signaturen verifiziert, Lieferungen dedupliziert und korrekt auf jedes Ereignis reagiert (payment_success, payment_failed, payment_reversal, payout_success, payout_failed, payout_refund).

Was ist Nomba Webhooks?

Dieses Skill befähigt Entwickler, einen sicheren und zuverlässigen Webhook-Handler für Nomba-Zahlungsbenachrichtigungen zu implementieren. Es unterstützt alle gängigen Frameworks wie Express, Next.js, NestJS, Fastify (Node.js), Flask, FastAPI, Django (Python) sowie Laravel, Symfony oder reines PHP. Der Kern des Skills ist die Verwendung der offiziellen SDKs zur Signaturverifikation, wodurch HMAC-Fehler und Replay-Angriffe vermieden werden. Er beschreibt detailliert die notwendigen Schritte: den rohen Anfragekörper vor dem Parsen verifizieren, Ereignisse anhand der `requestId` deduplizieren, sofort mit HTTP 200 antworten und aufwendige Arbeiten in Hintergrundjobs auslagern. Zudem erklärt er die korrekte Behandlung jedes Ereignistyps (z.B. keine Rückerstattung bei `payout_failed`, sondern erst bei `payout_refund`) und betont die erneute Abfrage der Nomba-API vor kritischen Aktionen, da bestimmte Felder wie der Betrag nicht signiert sind. Eine Checkliste und Testvektoren helfen bei der korrekten Implementierung und Fehlersuche.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/NombaOSS/skills/tree/HEAD/skills/nomba-webhooks

Installed? Explore more Codierung & Entwicklung skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Was macht Nomba Webhooks?

Ein Webhook ist die Art und Weise, wie Nomba deinem Server mitteilt, dass Geld tatsächlich bewegt wurde. Jeder im Internet kann eine POST-Anfrage an deine Webhook-URL senden, daher muss der Handler nachweisen, dass jede Anfrage tatsächlich von Nomba stammt, bevor er eine Bestellung als bezahlt markiert oder eine Rückerstattung vornimmt. Außerdem muss er mit wiederholten Zustellungen desselben Ereignisses durch Nomba umgehen können.

Verwende den offiziellen SDK-Verifier; implementiere das HMAC nicht selbst. Nombas Signatur deckt bestimmte Felder ab, die mit : verbunden sind, und die SDKs verhindern zudem einen Replay-Angriff, den eine naive Implementierung übersehen würde.

SpracheVerifikation
Node.js / TS (@nombaoss/nomba-node)verifyWebhook(rawBody, headers, signingKey)
Python (nomba)from nomba.webhooks import verify_signature → verify_signature(raw_body, headers, signing_key)
PHP (nombaoss/nomba-php)Nomba\Webhook::verify($rawBody, $headers, $signingKey)

Genaue Importe und Argumenttypen für jede Sprache findest du in references/sdk-setup.md. Jede Methode gibt das geparste Ereignis zurück oder wirft eine Ausnahme (WebhookVerificationError / WebhookVerificationException). Keine von ihnen benötigt API-Anmeldeinformationen, sondern nur den Signaturschlüssel aus dem Nomba-Dashboard (Entwickler → Webhook-Setup). Lies ihn aus einer Umgebungsvariable wie NOMBA_WEBHOOK_SIGNING_KEY. Es handelt sich um ein anderes Geheimnis als den API-Client-Secret.

Der Handler, Schritt für Schritt

  1. Den rohen Body abrufen. Verifiziere die genauen Bytes, die Nomba gesendet hat. Wenn ein JSON-Body-Parser zuerst ausgeführt wird und du das Objekt neu serialisierst, kann die Signatur nicht mehr übereinstimmen oder, schlimmer noch, du verifizierst etwas anderes als das, worauf du reagierst. Jedes Framework hat eine andere Möglichkeit, den rohen Body zu erhalten; siehe references/frameworks.md für die richtige Methode und schließe die Route vom CSRF-Schutz aus, wo das Framework einen solchen hat.
  2. Verifizieren, dann parsen. Verwende das vom Verifier zurückgegebene Ereignis. Bei Fehlschlag antworte mit 401 (oder 400) und stoppe. Protokolliere den vollständigen Body oder die Header abgelehnter Anfragen nicht auf Info-Ebene.
  3. Deduplizieren anhand von requestId. Nomba wiederholt Zustellungen, sodass dasselbe Ereignis mehrmals eintreffen kann. Zeichne jede verarbeitete requestId mit einer eindeutigen Einschränkung auf und überspringe bereits gesehene. Gib auch für Duplikate 200 zurück, sonst wiederholt Nomba die Zustellung weiter.
  4. Schnell bestätigen. Antworte umgehend mit 200 und erledige langsame Arbeiten (E-Mails, Fulfillment, Ledger-Updates) in einem Hintergrundjob, der mit der requestId verknüpft ist. Langsame Handler verursachen Timeouts und erneute Zustellungen.
  5. Auf das Ereignis reagieren unter Verwendung der folgenden Tabelle. Ordne das Ereignis deinem eigenen Datensatz anhand der Referenz zu und prüfe, ob der Betrag und die Währung deinen Erwartungen entsprechen, bevor du die Bestellung erfüllst.

Was jedes Ereignis bedeutet

event_typeAbgleich mitAktion
payment_successCheckout: data.order.orderReference; Virtuelles Konto / Überweisung: data.transaction.aliasAccountReference oder transactionIdAls bezahlt markieren, nachdem data.transaction.transactionAmount (und Währung) gegen deine Bestellung geprüft wurde. Bei tokenisierten Checkouts data.tokenizedCardData.tokenKey (falls vorhanden und nicht "N/A") zusammen mit dem Kunden speichern, nur serverseitig.
payment_failedwie obenDen fehlgeschlagenen Versuch markieren; den Kunden erneut versuchen lassen. Es wurde nichts abgebucht.
payment_reversalwie obenErhaltenes Geld wurde rückgängig gemacht. Fulfillment rückgängig machen oder zur Überprüfung markieren.
payout_successdata.transaction.merchantTxRefDie Überweisung oder Rechnungszahlung als zugestellt markieren.
payout_faileddata.transaction.merchantTxRefNoch nicht erstatten. Nomba kann es über einen anderen Weg erneut versuchen. Als fehlgeschlagen markieren und abwarten.
payout_refunddata.transaction.merchantTxRefDas Geld ist zurück in deiner Nomba-Wallet. Jetzt ist es sicher, deinem Kunden eine Rückerstattung zu geben oder gutzuschreiben und die Auszahlung als fehlgeschlagen zu markieren.

Eine Rückerstattung bei payout_failed (oder bei einem Timeout) anstatt bei payout_refund ist der klassische Weg, wie Händler Kunden doppelt bezahlen. Bei jedem nicht erkannten Ereignis protokolliere es und gib 200 zurück.

Bestätige Geldentscheidungen mit der API. Nombas Signatur deckt nur event_type, requestId, data.merchant.userId, data.merchant.walletId, data.transaction.transactionId, type, time, responseCode und den Zeitstempel ab. Der Betrag, orderReference, merchantTxRef und der Karten-tokenKey sind nicht signiert. Bevor du also eine Bestellung erfüllst, eine Rückerstattung vornimmst oder einen Kartentoken speicherst, frage mit dem SDK erneut an und handle basierend auf der API-Antwort: transactions.get({ orderReference }) für Checkout-Zahlungen (auch checkout.getTransaction in der Produktion) und transactions.get({ merchantTxRef }) für Auszahlungen. Die Deduplizierung anhand der signierten requestId verhindert eine manipulierte Kopie eines bereits verarbeiteten Ereignisses, und die erneute Abfrage deckt den Rest ab.

Replay-Schutz

Die Deduplizierung anhand der requestId ist die Hauptverteidigung: Eine wiederholte oder erneut zugestellte Lieferung eines bereits verarbeiteten Ereignisses ändert nichts.

Die Verifier können auch alte Zeitstempel ablehnen (toleranceSeconds / tolerance_seconds / das vierte Argument in PHP), aber lass dies standardmäßig deaktiviert. Nomba dokumentiert nomba-timestamp als die Zeit, zu der die Nutzlast gesendet wurde, ohne zu sagen, ob automatische Wiederholungen einen neuen Zeitstempel erhalten. Wenn sie den ursprünglichen behalten, würde eine Toleranz, die kürzer ist als Nombas Wiederholungsfenster, legitime Wiederholungen nach einem Ausfall ablehnen, und bezahlte Bestellungen würden nie bestätigt. Aktiviere sie erst, nachdem du in der Sandbox bestätigt hast, dass erneute Zustellungen einen neuen Zeitstempel tragen, und halte sie dann großzügig (z. B. eine Stunde) bei synchronisierten Serveruhren.

Beispiel (Node.js + Express)

import express from "express";
import { verifyWebhook, WebhookEventType } from "@nombaoss/nomba-node";

const app = express();

// Raw body for this route only; register it before any app-wide express.json().
app.post("/webhooks/nomba", express.raw({ type: "*/*" }), async (req, res) => {
  let event;
  try {
    event = verifyWebhook(req.body, req.headers, process.env.NOMBA_WEBHOOK_SIGNING_KEY!);
  } catch {
    return res.sendStatus(401);
  }

  // Unique constraint on request_id; returns false if we've seen it.
  const isNew = await webhookEvents.recordOnce(event.requestId, event.event_type);
  if (isNew) await jobs.enqueue("nomba-webhook", event); // process in the background
  res.sendStatus(200);
});

Python (Flask/FastAPI/Django), PHP (Laravel/plain) und andere Node-Frameworks sind in references/frameworks.md.

Testen

  • Teste den Handler mit dem vom SDK dokumentierten Testvektor: Body aus der webhook-fixture.json des SDKs, Schlüssel SampleSignatureKey123, Header nomba-timestamp: 2025-09-29T10:51:44Z, nomba-signature: q30yFZDyykoymBf9+mt1wz5UAuLk57raPGPHlGzIv00=. Stelle sicher, dass ein falscher Schlüssel, ein manipulierter Body und fehlende Header abgelehnt werden und dass eine doppelte requestId nur einmal verarbeitet wird.
  • End-to-End: Setze eine Test-Webhook-URL und einen Signaturschlüssel im Dashboard, mache deinen lokalen Server mit einem Tunnel zugänglich und tätige Sandbox-Zahlungen. Das Dashboard kann Zustellungen erneut pushen oder debuggen (https://developer.nomba.com/docs/api-basics/troubleshoot-webhooks).

Checkliste

  • Der rohe Body wird mit dem SDK verifiziert, bevor irgendein Parsing oder eine Geschäftslogik ausgeführt wird.
  • Der Signaturschlüssel stammt aus der Umgebung, nicht aus dem Code.
  • Schlechte Signaturen erhalten 401/400; es passiert nichts anderes.
  • requestId wird mit einer eindeutigen Einschränkung dedupliziert; Duplikate erhalten dennoch 200.
  • Schwere Arbeiten laufen im Hintergrund; der Handler gibt schnell 200 zurück.
  • Betrag und Währung werden vor der Erfüllung gegen die Bestellung geprüft.
  • Rückerstattungen erfolgen nur bei payout_refund (oder einer erneuten Abfrage, die REFUND zurückgibt).
  • Die Route ist vom CSRF-Schutz ausgeschlossen und verwendet HTTPS.

Verwandte Skills