Webhooks
Actualizado:
En lugar de realizar consultas periódicas, puedes recibir los eventos de un pedido en tu punto final HTTPS. Los webhooks transmiten los mismos eventos que Ordenar eventos. El registro de eventos sigue estando disponible tanto si hay webhooks como si no, por lo que siempre puedes recurrir al sondeo.
Configuración
Aceptado
callback_urlen Realizar un pedido, o configúralo más tarde con Orden de actualización.La respuesta devuelve
webhook_secret(whsec_…) una vez. Guárdalo: firma cada webhook.Cambiar
callback_urlemite un nuevo secreto. El secreto anterior sigue firmando los webhooks durante 24 horas: durante este tiempoX-PS-SignatureLleva dos firmas.
Solicitud
Propiedad | Valor |
|---|---|
Método |
|
Encabezados |
|
Cuerpo | Un evento, como en Ordenar eventos; |
Éxito | Cualquier |
Entrega | Al menos una vez, sin orden concreto. Eliminar duplicados mediante |
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— los bytes (UTF-8) del conjuntowebhook_secretcadena, incluyendo elwhsec_prefijo.timestamp— el valor deX-PS-Timestamp, segundos Unix.raw_body— el cuerpo tal y como se ha recibido, antes de cualquier análisis JSON.Durante las 24 horas posteriores a un cambio secreto, el encabezado es
v1=<current>,v1=<previous>: acepta la solicitud si alguna de ellas coincide.Rechaza las solicitudes cuya marca de tiempo difiera de tu reloj en más de 300 segundos.
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)));
}Reintentos
Regla | Valor |
|---|---|
Primer intento | No antes de 30 segundos tras el suceso |
Reintentos | Después de 1 min, 5 min, 30 min, 2 h, 6 h, 12 h y 24 h: 8 intentos en unas 44,6 horas |
Demasiado antiguo | Un evento que tenga más de unas 45,6 horas ya no se envía. |
Disyuntor | Tras tres fallos consecutivos, el resto de eventos de la secuencia esperan al siguiente intento. |
Desactivar | Si el último intento de un evento falla y la URL no ha aceptado ningún webhook desde que se creó dicho evento, se desactiva el envío a la URL. El pedido en sí no se ve afectado. |
Para habilitar una URL desactivada, envíala de nuevo con Orden de actualización: la misma URL como máximo una vez por hora; una nueva URL en cualquier momento. Los eventos perdidos siempre se encuentran en Ordenar eventos.
Normas de callback_url
Se comprueba en el momento de emitir el presupuesto y en PATCH. Cualquier incumplimiento dará lugar a 400 invalid_request:
Regla | detalle |
|---|---|
ASCII imprimible, hasta 2048 caracteres |
|
|
|
No incluyas nombre de usuario ni contraseña en la URL. |
|
Sin fragmento |
|
Solo puerto 443 |
|
Un nombre DNS público, una dirección IPv4 canónica o una dirección IPv6 entre paréntesis |
|
Todas las direcciones del servidor son públicas |
|
No es un proveedor del servicio en sí mismo |
|
El DNS se comprueba de nuevo antes de cada envío; la solicitud se envía únicamente a las direcciones comprobadas.