Webhooks
Selgeo invia notifiche HTTP POST in tempo reale al tuo server quando si verificano eventi nel tuo account. Usa i webhook per automatizzare i flussi di lavoro come il provisioning dell'accesso quando un partner viene approvato, la sincronizzazione delle commissioni con il tuo sistema contabile o l'allerta del tuo team in caso di frode.
Tutti i payload dei webhook usano API versione v1.
Registrazione degli endpoint
Registra gli endpoint webhook dalla pagina Impostazioni > Webhooks nella dashboard del commerciante.
- Fai clic su Aggiungi endpoint.
- Inserisci l'URL dove vuoi ricevere gli eventi. Gli endpoint in modalità live devono usare HTTPS.
- Seleziona gli eventi a cui vuoi iscriverti.
- Fai clic su Crea. Il tuo segreto di firma (
whsec_...) viene mostrato una volta — copialo e conservalo in modo sicuro.
Verifica della firma
Ogni richiesta webhook include un'intestazione X-Selgeo-Signature:
t=<unix_timestamp>,v1=<hmac_hex>
L'HMAC è calcolato come HMAC-SHA256(signing_secret_bytes, "<timestamp>.<raw_json_body>").
Esempi di verifica
- 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);
}
Catalogo degli eventi
Eventi partner
| Evento | Descrizione |
|---|---|
participant.created | Un nuovo partner ha fatto domanda o è stato aggiunto a un programma |
participant.approved | Un partner è stato approvato |
participant.rejected | Una domanda di partner è stata rifiutata |
participant.suspended | Un partner attivo è stato sospeso |
participant.reinstated | Un partner sospeso è stato reintegrato |
participant.erased | I dati di un partner sono stati cancellati (GDPR) |
Eventi di attribuzione
| Evento | Descrizione |
|---|---|
attribution.created | Un clic è stato attribuito a un partner |
attribution.converted | Un clic attribuito ha portato a una conversione |
attribution.expired | Una finestra di attribuzione è scaduta senza conversione |
Eventi di conversione
| Evento | Descrizione |
|---|---|
conversion.created | È stata registrata una nuova conversione |
conversion.fraud_detected | È stata rilevata una frode (auto-referral o conversione duplicata) |
Eventi di commissione
| Evento | Descrizione |
|---|---|
commission.created | È stata calcolata una nuova commissione |
commission.approved | Una commissione è stata approvata per il pagamento |
commission.rejected | Una commissione è stata rifiutata |
commission.refunded | Una commissione è stata stornata a causa di un rimborso |
Politica di nuovo tentativo
Se il tuo endpoint restituisce un codice di stato non-2xx o la richiesta scade (30 secondi), Selgeo riprova con backoff esponenziale:
| Tentativo | Ritardo dopo il fallimento |
|---|---|
| 1° tentativo | 1 minuto |
| 2° tentativo | 2 minuti |
| 3° tentativo | 4 minuti |
| 4° tentativo | 15 minuti |
Dopo 5 tentativi totali, la consegna passa allo stato dead-letter.
Best practice
- Restituire 200 rapidamente. Elabora l'evento in modo asincrono in un job in background.
- Gestire i duplicati. Usa il campo
event_idper la deduplicazione. - Verificare le firme. Verifica sempre l'intestazione
X-Selgeo-Signature. - Usare HTTPS in produzione. Gli endpoint in modalità live richiedono HTTPS.