Webhook
Updated:
Invece di effettuare il polling, è possibile ricevere gli eventi di un ordine sul proprio endpoint HTTPS. I webhook trasmettono gli stessi eventi di Ordine degli eventi. Il registro degli eventi rimane disponibile sia con che senza webhook, quindi è sempre possibile ricorrere al polling.
Configurazione
Approvato
callback_urlin Effettua un ordine, oppure impostalo in un secondo momento con Ordine di aggiornamento.La risposta restituisce
webhook_secret(whsec_…) una volta sola. Salvarla: serve a firmare ogni webhook.Modifica
callback_urlemette un nuovo segreto. Il segreto precedente continua a firmare i webhook per 24 ore: durante questo periodoX-PS-Signaturepresenta due firme.
Richiesta
Proprietà | Valore |
|---|---|
Metodo |
|
Intestazioni |
|
Corpo del testo | Un evento, come in Ordine degli eventi; |
Successo | Qualsiasi |
Consegna | Almeno una volta, in ordine casuale. Eliminare i duplicati in base a |
POST /hooks/proxy HTTP/1.1
Host: agent.example.com
Content-Type: application/json; charset=utf-8
User-Agent: ProxySeller-Webhooks/1
X-PS-Timestamp: 1762077838
X-PS-Signature: v1=5f0c2a…9e
{"event_id":"evt_01JZ8QA2C4E6G8J0K2M4P6R8T0","type":"order.active","created_at":"2026-11-02T10:03:58Z","api_version":"v1","order_id":"ord_01JZ8Q7K5N2R4T6V8X0Z2B4D6F","wallet":"0x857b06519e91e3a54538791bdbb0e22373e36b66","attempt":1,"data":{"lines":[{"line_id":"1","status":"active","delivered":2,"qty":2}],"expires_at":"2026-12-02T23:59:59Z"}}Verifica la firma
X-PS-Signature: v1=hex(HMAC-SHA256(key, timestamp + "." + raw_body))
key— i byte (UTF-8) dell’interowebhook_secretstringa, compresa lawhsec_prefisso.timestamp— il valore diX-PS-Timestamp, secondi Unix.raw_body— il corpo del testo esattamente come ricevuto, prima di qualsiasi analisi JSON.Nelle 24 ore successive a una modifica segreta, l'intestazione è
v1=<current>,v1=<previous>: accetta la richiesta se uno qualsiasi di essi corrisponde.Rifiuta le richieste il cui timestamp differisca dal tuo orologio di oltre 300 secondi.
Python
import hashlib
import hmac
import time
def verify(secret: str, timestamp: str, signature_header: str, raw_body: bytes, tolerance: int = 300) -> bool:
if abs(time.time() - int(timestamp)) > tolerance:
return False
expected = hmac.new(secret.encode(), timestamp.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
candidates = [part.strip()[3:] for part in signature_header.split(",") if part.strip().startswith("v1=")]
return any(hmac.compare_digest(expected, candidate) for candidate in candidates)Node.js
import crypto from 'node:crypto';
function verify(secret, timestamp, signatureHeader, rawBody, toleranceSeconds = 300) {
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > toleranceSeconds) return false;
const expected = crypto.createHmac('sha256', secret).update(`${timestamp}.`).update(rawBody).digest('hex');
return signatureHeader.split(',').map((s) => s.trim()).filter((s) => s.startsWith('v1='))
.some((s) => s.length === expected.length + 3
&& crypto.timingSafeEqual(Buffer.from(s.slice(3)), Buffer.from(expected)));
}Nuovi tentativi
Regola | Valore |
|---|---|
Primo tentativo | Non prima di 30 secondi dopo l’evento |
Nuovi tentativi | Dopo 1 min, 5 min, 30 min, 2 h, 6 h, 12 h e 24 h: 8 tentativi in circa 44,6 ore |
Troppo vecchio | Un evento risalente a più di circa 45,6 ore fa non viene più inviato |
Interruttore automatico | Dopo 3 fallimenti consecutivi, gli altri eventi dell’ordine attendono il prossimo tentativo |
Disattivazione | Se l’ultimo tentativo di un evento fallisce e l’URL non ha accettato alcun webhook da quando tale evento è stato creato, l’invio all’URL viene disabilitato. L’ordine stesso non ne risente |
Per abilitare un URL disabilitato, inviarlo nuovamente con Ordine di aggiornamento: lo stesso URL al massimo una volta all’ora, un nuovo URL in qualsiasi momento. Gli eventi mancati sono sempre in Ordine degli eventi.
Regole relative a callback_url
Verificato al momento dell’emissione del preventivo e in PATCH. Una violazione comporta 400 invalid_request:
Regola | dettaglio |
|---|---|
ASCII stampabile, fino a 2048 caratteri |
|
|
|
Nessun nome utente o password nell’URL |
|
Nessun frammento |
|
Solo porta 443 |
|
Un nome DNS pubblico, oppure un indirizzo IPv4 canonico, oppure un indirizzo IPv6 tra parentesi |
|
Tutti gli indirizzi dell’host sono pubblici |
|
Non un host del servizio stesso |
|
Il DNS viene ricontrollato prima di ogni consegna; la richiesta viene inviata solo agli indirizzi verificati.