Webhooks
Selgeo stuurt real-time HTTP POST-meldingen naar uw server wanneer er gebeurtenissen in uw account plaatsvinden. Gebruik webhooks om workflows te automatiseren, zoals toegang verlenen wanneer een partner wordt goedgekeurd, commissies synchroniseren met uw boekhoudsysteem of uw team waarschuwen voor fraude.
Alle webhook-payloads gebruiken API-versie v1.
Eindpuntregistratie
Registreer webhook-eindpunten op de pagina Instellingen > Webhooks in het verkopersdashboard.
- Klik op Eindpunt toevoegen.
- Voer de URL in waar u gebeurtenissen wilt ontvangen. Live-modus-eindpunten moeten HTTPS gebruiken.
- Selecteer de gebeurtenissen waarop u zich wilt abonneren.
- Klik op Aanmaken. Uw ondertekeningsgeheim (
whsec_...) wordt eenmalig weergegeven — kopieer het en bewaar het veilig.
Handtekeningverificatie
Elk webhookverzoek bevat een X-Selgeo-Signature-header:
t=<unix_timestamp>,v1=<hmac_hex>
De HMAC wordt berekend als HMAC-SHA256(signing_secret_bytes, "<timestamp>.<raw_json_body>").
Verificatievoorbeelden
- Node.js
- Python
- PHP
import crypto from 'node:crypto';
function verifyWebhookSignature(signingSecret, signatureHeader, rawBody) {
if (!signatureHeader) return false;
const rawSecret = signingSecret.replace(/^whsec_/, '');
const secretBytes = Buffer.from(rawSecret, 'hex');
const parts = Object.fromEntries(
signatureHeader.split(',').map((part) => part.split('=', 2))
);
const timestamp = parts.t;
const receivedHmac = parts.v1;
if (!timestamp || !receivedHmac) return false;
const age = Math.floor(Date.now() / 1000) - parseInt(timestamp, 10);
if (age > 300 || age < -30) return false;
const expectedHmac = crypto
.createHmac('sha256', secretBytes)
.update(`${timestamp}.${rawBody}`)
.digest('hex');
const receivedBuf = Buffer.from(receivedHmac, 'hex');
const expectedBuf = Buffer.from(expectedHmac, 'hex');
if (receivedBuf.length !== expectedBuf.length) return false;
return crypto.timingSafeEqual(receivedBuf, expectedBuf);
}
import hashlib
import hmac
import time
def verify_webhook_signature(signing_secret: str, signature_header: str, raw_body: str) -> bool:
if not signature_header:
return False
raw_secret = signing_secret.removeprefix("whsec_")
secret_bytes = bytes.fromhex(raw_secret)
try:
parts = dict(part.split("=", 1) for part in signature_header.split(","))
except ValueError:
return False
timestamp = parts.get("t")
received_hmac = parts.get("v1")
if not timestamp or not received_hmac:
return False
try:
age = int(time.time()) - int(timestamp)
except ValueError:
return False
if age > 300 or age < -30:
return False
message = f"{timestamp}.{raw_body}".encode()
expected_hmac = hmac.new(secret_bytes, message, hashlib.sha256).hexdigest()
return hmac.compare_digest(received_hmac, expected_hmac)
<?php
function verifyWebhookSignature(string $signingSecret, string $signatureHeader, string $rawBody): bool {
if ($signatureHeader === '') return false;
$rawSecret = str_starts_with($signingSecret, 'whsec_') ? substr($signingSecret, 6) : $signingSecret;
$secretBytes = hex2bin($rawSecret);
if ($secretBytes === false) return false;
$parts = [];
foreach (explode(',', $signatureHeader) as $part) {
$segments = explode('=', $part, 2);
if (count($segments) !== 2) continue;
[$key, $value] = $segments;
$parts[$key] = $value;
}
$timestamp = $parts['t'] ?? null;
$receivedHmac = $parts['v1'] ?? null;
if (!$timestamp || !$receivedHmac) return false;
$age = time() - (int) $timestamp;
if ($age > 300 || $age < -30) return false;
$expectedHmac = hash_hmac('sha256', "{$timestamp}.{$rawBody}", $secretBytes);
return hash_equals($expectedHmac, $receivedHmac);
}
Gebeurteniscatalogus
Partnergebeurtenissen
| Gebeurtenis | Beschrijving |
|---|---|
participant.created | Een nieuwe partner heeft een aanvraag ingediend of is toegevoegd aan een programma |
participant.approved | Een partner is goedgekeurd |
participant.rejected | Een partneraanvraag is afgewezen |
participant.suspended | Een actieve partner is geschorst |
participant.reinstated | Een geschorste partner is hersteld |
participant.erased | De gegevens van een partner zijn gewist (AVG) |
Attributiegebeurtenissen
| Gebeurtenis | Beschrijving |
|---|---|
attribution.created | Een klik is toegeschreven aan een partner |
attribution.converted | Een toegeschreven klik heeft geleid tot een conversie |
attribution.expired | Een attributievenster is verlopen zonder conversie |
Conversiegebeurtenissen
| Gebeurtenis | Beschrijving |
|---|---|
conversion.created | Een nieuwe conversie is geregistreerd |
conversion.fraud_detected | Fraude is gedetecteerd (zelf-referral of dubbele conversie) |
Commissiegebeurtenissen
| Gebeurtenis | Beschrijving |
|---|---|
commission.created | Een nieuwe commissie is berekend |
commission.approved | Een commissie is goedgekeurd voor uitbetaling |
commission.rejected | Een commissie is afgewezen |
commission.refunded | Een commissie is teruggedraaid vanwege een terugbetaling |
Beleid voor opnieuw proberen
Als uw eindpunt een niet-2xx-statuscode retourneert of het verzoek een time-out heeft (30 seconden), probeert Selgeo het opnieuw met exponentiële vertraging:
| Poging | Vertraging na mislukking |
|---|---|
| 1e poging | 1 minuut |
| 2e poging | 2 minuten |
| 3e poging | 4 minuten |
| 4e poging | 15 minuten |
Na 5 pogingen totaal gaat de bezorging naar de dead-letter-status.
Beste praktijken
- Snel 200 retourneren. Verwerk de gebeurtenis asynchroon in een achtergrondtaak.
- Duplicaten verwerken. Gebruik het veld
event_idvoor deduplicatie. - Handtekeningen verifiëren. Verifieer altijd de
X-Selgeo-Signature-header. - HTTPS gebruiken in productie. Live-modus-eindpunten vereisen HTTPS.