Manejo de Webhooks
Usa los handlers de webhooks del SDK para Next.js, Hono y Express para recibir y procesar eventos de Whatalo con verificación de firma integrada.
@whatalo/plugin-sdk incluye handlers de webhooks específicos para cada framework que manejan la verificación de firma, el parseo de JSON y el enrutamiento de eventos por ti. Elige el adaptador que corresponda a tu framework de servidor.
Next.js (App Router)
// app/api/webhooks/route.ts
import { createWebhookHandler } from "@whatalo/plugin-sdk/adapters/nextjs";
export const POST = createWebhookHandler({
secret: process.env.WHATALO_CLIENT_SECRET!,
handlers: {
"order.created": async (payload) => {
console.log("New order:", payload.data.id);
// Trigger fulfilment, send confirmation email, etc.
},
"order.status_changed": async (payload) => {
console.log("Status changed:", payload.data.status);
},
"product.updated": async (payload) => {
console.log("Product changed:", payload.data.id);
// Sync with your catalogue cache
},
"app.installed": async (payload) => {
// Provision resources for the new merchant
await provisionMerchant(payload.store);
},
"app.uninstalled": async (payload) => {
// Clean up stored merchant data
await cleanupMerchant(payload.store);
},
},
onUnhandledEvent: async (event, payload) => {
// Called for any event not listed in handlers above
console.log(`Unhandled event: ${event}`);
},
});El handler lee el cuerpo de la solicitud sin procesar antes de parsearlo, lo cual es necesario para la verificación HMAC correcta. No envuelvas esta ruta en ningún middleware de parseo de body.
Hono
import { Hono } from "hono";
import { createWebhookHandler } from "@whatalo/plugin-sdk/adapters/hono";
const app = new Hono();
const handler = createWebhookHandler({
secret: process.env.WHATALO_CLIENT_SECRET!,
handlers: {
"order.created": async (payload) => {
await processNewOrder(payload.data);
},
"customer.created": async (payload) => {
await syncCustomer(payload.data);
},
},
});
app.post("/api/webhooks", handler);
export default app;Express
import express from "express";
import { createWebhookHandler } from "@whatalo/plugin-sdk/adapters/express";
const app = express();
// Mount BEFORE any global body-parsing middleware on this route
const handler = createWebhookHandler({
secret: process.env.WHATALO_CLIENT_SECRET!,
handlers: {
"order.created": async (payload) => {
await processNewOrder(payload.data);
},
},
});
app.post("/api/webhooks", handler);Si usas express.json() globalmente, excluye la ruta de webhooks:
// Apply JSON parsing to all routes except /api/webhooks
app.use((req, res, next) => {
if (req.path === "/api/webhooks") return next();
express.json()(req, res, next);
});Opciones del Handler
| Opción | Tipo | Requerido | Descripción |
|---|---|---|---|
secret | string | Sí | El secreto de cliente de tu plugin, usado para verificación de firma HMAC |
handlers | object | Sí | Mapa de tipo de evento → función handler async |
onUnhandledEvent | function | No | Catch-all llamado para eventos no listados en handlers |
Firma del Handler
Cada handler recibe un único argumento tipado payload:
type WebhookPayload = {
id: string; // Unique delivery ID (X-Webhook-ID)
event: string; // Event type (X-Webhook-Event)
store: string; // Store identifier
timestamp: string; // ISO 8601 timestamp
data: unknown; // Event-specific data (type depends on event)
};Manejo de Errores en Handlers
Si un handler lanza un error, el adaptador lo captura, registra el stack trace y devuelve 500 para activar un reintento de Whatalo. Para ignorar silenciosamente un evento sin activar un reintento, retorna normalmente sin lanzar:
"order.created": async (payload) => {
const order = payload.data as OrderPayload;
// If the order is already in our system, skip silently
const exists = await db.orders.findUnique({ where: { id: order.id } });
if (exists) return; // Returns 200 OK — no retry
// Process the new order
await db.orders.create({ data: mapOrder(order) });
},Verificación Manual (Sin Adaptador de Framework)
Si usas un framework no listado aquí, verifica las firmas manualmente usando verifyWebhook:
import { verifyWebhook } from "@whatalo/plugin-sdk/webhooks";
// rawBody must be the raw string body — read it before JSON.parse
const isValid = verifyWebhook({
payload: rawBody,
signature: req.headers["x-webhook-signature"] as string,
secret: process.env.WHATALO_CLIENT_SECRET!,
});
if (!isValid) {
return res.status(401).json({ error: "Invalid signature" });
}
const event = JSON.parse(rawBody);Consulta Verificación y Seguridad para más detalles sobre el algoritmo de firma y la protección contra replay attacks.