Todos los artículos
Eventos de webhook de suscripción y licencia que fluyen a través de un túnel HTTPS hacia un manejador de desarrollo local verificado.
Lemon Squeezywebhookssubscriptionslocal testing

Probar webhooks de Lemon Squeezy en localhost

Para probar los webhooks de Lemon Squeezy en localhost, inicia tu aplicación, expón su ruta de webhook con un túnel HTTPS público, crea un webhook en modo de prueba en Configuración → Webhooks, y verifica el cuerpo de la solicitud sin procesar contra el X-Signature encabezado. Utiliza una prueba de pago para datos de pedidos reales y los controles de simulación de suscripción de Lemon Squeezy para eventos del ciclo de vida que son difíciles de esperar.

Cómo se ve una configuración local correcta

Lemon Squeezy envía un HTTP POST desde su infraestructura, por lo que no puede alcanzar localhost en tu computadora. El túnel termina el TLS público y envía el método, el cuerpo y los encabezados sin cambios a una ruta local como http://localhost:3000/api/webhooks/lemonsqueezy.

Una prueba completa tiene cuatro partes: modo de prueba Lemon Squeezy, una URL de callback pública, el mismo secreto de firma en el panel de control y en el entorno local, y un manejador que verifica antes de cambiar el estado de la suscripción o la licencia. Esto es más útil que publicar JSON copiado con curl, porque una entrega real prueba tanto el enrutamiento como la firma generada por el proveedor.

Crea el webhook y envía un evento de prueba

  1. Ejecute su aplicación en el puerto 3000 y verifique que la ruta acepte POST.
  2. Comienza el túnel con npx portpreview 3000.
  3. Cambia el panel de Lemon Squeezy al modo de prueba.
  4. Abre Configuración → Webhooks y agrega https://your-subdomain.portpreview.dev/api/webhooks/lemonsqueezy.
  5. Genera un secreto de firma aleatorio fuerte, guárdalo como LEMONSQUEEZY_WEBHOOK_SECRET, y seleccione solo los eventos que su aplicación consume.
  6. Crea una compra de prueba o utiliza la acción de simulación de suscripción en modo de prueba disponible, luego inspecciona el registro de entregas y tu respuesta local.

El oficial Documentación del webhook de Lemon Squeezy te permite inspeccionar cargas útiles recientes y reenviar webhooks grabados desde la página de configuración. Un reenvío es valioso para las pruebas de regresión, pero tu aplicación debe tratarlo como un duplicado en lugar de un segundo evento de negocio.

Verificar X-Signature antes de analizar JSON

Lemon Squeezy calcula un resumen HMAC-SHA256 sobre la carga útil de la solicitud usando el secreto de firma y envía el resumen en hexadecimal en X-Signature. Calcula el resumen sobre los bytes exactos del cuerpo sin procesar y compara búferes de igual longitud con una función de tiempo constante. El principal documentación de solicitudes de firma incluye ejemplos para Node, PHP, Python y Ruby.

// app/api/webhooks/lemonsqueezy/route.ts
import crypto from 'node:crypto';
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const secret = process.env.LEMONSQUEEZY_WEBHOOK_SECRET;
  if (!secret) return new NextResponse('server misconfigured', { status: 500 });

  const rawBody = await request.text();
  const suppliedHex = request.headers.get('x-signature') ?? '';

  if (!/^[0-9a-f]{64}$/i.test(suppliedHex)) {
    return new NextResponse('invalid signature', { status: 401 });
  }

  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest();
  const supplied = Buffer.from(suppliedHex, 'hex');

  if (supplied.length !== expected.length ||
      !crypto.timingSafeEqual(supplied, expected)) {
    return new NextResponse('invalid signature', { status: 401 });
  }

  const event = JSON.parse(rawBody);
  await acceptEvent(event);
  return NextResponse.json({ received: true });
}

No llames request.json() primero. Re-serializar el objeto resultante puede cambiar bytes que parecen insignificantes y romper el HMAC. La verificación de longitud también es esencial porque Node's timingSafeEqual lanza cuando las longitudes del búfer difieren. Oficial de Lemon Squeezy Tutorial de webhook de Next.js sigue la misma secuencia de cuerpo en bruto.

Comprenda la carga útil antes de actualizar su base de datos

El cuerpo es un objeto de recurso JSON:API. meta.event_name identifica el evento, meta.custom_data transporta datos pasados a través del proceso de pago, y data contiene el recurso de pedido, suscripción o clave de licencia. Lemon Squeezy también envía X-Event-Name; trate el cuerpo firmado como la entrada autorizada y use el encabezado para diagnósticos de enrutamiento.

Pedidos e identidad del cliente

Para order_created, asigna la compra a un usuario interno usando un identificador generado por el servidor en los datos personalizados de pago. No confíes en un ID de usuario proporcionado por el cliente sin verificar la propiedad. Guarda el ID de recurso y el ID de variante de Lemon Squeezy para que los futuros eventos de suscripción o reembolso puedan reconciliarse.

Las suscripciones son una máquina de estados

Mango subscription_created, subscription_updated, subscription_cancelled, subscription_resumed, y subscription_expired como transiciones distintas. La cancelación puede significar que el acceso sigue siendo válido hasta el final del período de facturación; la expiración es la señal más fuerte de que la suscripción ha terminado. Modele fechas y estado en lugar de alternar un valor booleano.

Fallos de pago y recuperación

Pruebas de flujos de renovación fallidos y recuperados donde el modo de prueba los soporta. Un pago fallido no debería necesariamente revocar el acceso de inmediato; aplique el período de gracia definido por la política de su producto y permita que pagos posteriores o eventos de suscripción ajusten la cuenta al estado más reciente del proveedor.

Los reintentos requieren un procesamiento idempotente

Según Lemon Squeezy referencia de solicitud de webhook, a 200 Las marcas de respuesta capturan como exitoso. Otros estados se reintentan hasta tres veces más con retrasos exponenciales mostrados aproximadamente como 5, 25 y 125 segundos. El reenvío del panel crea otra razón por la que el mismo evento lógico puede llegar más de una vez.

Construya una clave de idempotencia a partir de campos de carga útil firmados estables. Un enfoque práctico es una fila única en la base de datos para el ID del recurso del webhook, el nombre del evento y la marca de tiempo del evento, o un hash del cuerpo firmado sin procesar cuando no hay un ID de evento dedicado presente. Inserte ese recibo y actualice el estado de la aplicación en una sola transacción. Devuelva 200 para un duplicado completado previamente.

BEGIN;
INSERT INTO webhook_receipts (fingerprint, received_at)
VALUES ($1, now())
ON CONFLICT DO NOTHING;
-- Continue only if one row was inserted.
UPDATE subscriptions SET provider_status = $2 WHERE provider_id = $3;
COMMIT;

No haga que la entrega de correos electrónicos o una llamada lenta a una API de terceros forme parte del camino de reconocimiento. Almacene un trabajo en la misma transacción, responda y deje que un trabajador maneje los efectos secundarios. El más amplio guía de reintento de webhook e idempotencia cubre este patrón.

Solución de problemas de las pruebas locales de webhooks de Lemon Squeezy

Ninguna solicitud llega a la aplicación

Comprueba que el túnel sigue funcionando, que la devolución de llamada contiene el sufijo de la ruta y que tu framework tiene un manejador POST en esa ruta exacta. Llama al endpoint público tú mismo. Un túnel 502 apunta al puerto local; un framework 404 apunta a la colocación de la ruta.

Cada firma es inválida

Confirma que el modo de prueba y el modo en vivo no estén utilizando diferentes registros de webhook o secretos. Lee el cuerpo una vez como texto o bytes, no recortes ni el cuerpo ni el secreto, y asegúrate de que el middleware global de JSON no se ejecute primero. Inspecciona si X-Signature llegó, pero nunca imprimas el secreto de firma.

La entrega sigue reintentando

Lemon Squeezy espera 200, no una redirección a una página de inicio de sesión. Excluya la ruta del webhook del middleware de CSRF y de la sesión de usuario mientras se mantiene la verificación de la firma. Capture los errores de la base de datos, mantenga el trabajo corto y asegúrese de que se devuelva una respuesta de éxito en cada rama de evento manejada.

La simulación de suscripción no está disponible

Los controles de simulación requieren datos de suscripción en modo de prueba adecuados. Algunas simulaciones de pago necesitan historial de renovaciones. Cree primero la suscripción de prueba o compra de prueba requerida, luego use el menú de acciones de suscripción descrito en Lemon Squeezy. guía de desarrollador de webhook.

Lista de verificación de seguridad y puesta en marcha

  • Genera un secreto de firma aleatorio, guárdalo en la configuración del entorno y rótalo si se expone.
  • Verifique el HMAC sobre el cuerpo bruto antes de analizar JSON, escribir en la base de datos, generar licencias o realizar cambios de acceso.
  • Use una comparación segura en el tiempo y rechace firmas faltantes o mal formadas.
  • Mantenga los secretos y registros del modo de prueba y del webhook en vivo separados.
  • Permitir solo POST, aplicar un límite de tamaño de cuerpo razonable, limitar la frecuencia de solicitudes inválidas y ocultar los datos de los clientes en los registros.
  • Prueba duplicados, eventos del ciclo de vida fuera de orden, tiempo de inactividad de la base de datos y recuperación de tiempo de espera antes de reemplazar el túnel con tu URL de producción.

Un proceso de pago exitoso y sencillo solo demuestra que llegó un evento. Una integración lista para producción demuestra el rechazo de firmas, la seguridad frente a duplicados, la convergencia del ciclo de vida y la recuperación de una respuesta que no sea 200. Para los problemas criptográficos subyacentes, vea el guía de verificación de firmas.

Preguntas frecuentes

¿Cómo pruebo los webhooks de Lemon Squeezy en localhost?
Expón tu ruta de webhook local con un túnel HTTPS, crea un webhook mientras el panel está en modo de prueba, usa el mismo secreto de firma localmente y activa una simulación de pago de prueba o suscripción compatible.
¿Cómo se verifica la firma de un webhook de Lemon Squeezy?
Calcula un digest HMAC-SHA256 sobre el cuerpo de la solicitud exacto en bruto con tu secreto de firma, decodifica el valor hexadecimal de X-Signature y compara buffers de igual longitud con una función segura contra temporización.
¿Lemon Squeezy reintenta los webhooks fallidos?
Sí. Su documentación dice que las respuestas distintas de 200 se reintentan hasta tres veces adicionales con retroceso exponencial, aproximadamente después de 5, 25 y 125 segundos.
¿Puedo simular renovaciones de suscripción de Lemon Squeezy en modo de prueba?
El modo de prueba proporciona acciones de simulación para los eventos de suscripción compatibles. Algunas simulaciones relacionadas con pagos requieren una suscripción de prueba existente y datos de renovación, por lo que primero cree el estado de prueba necesario.