Tous les articles
Événements de webhook d'abonnement et de licence transitant par un tunnel HTTPS vers un gestionnaire de développement local vérifié.
Lemon Squeezywebhookssubscriptionslocal testing

Tester les webhooks Lemon Squeezy sur localhost

Pour tester les webhooks Lemon Squeezy sur localhost, démarrez votre application, exposez sa route webhook avec un tunnel HTTPS public, créez un webhook en mode Test dans Paramètres → Webhooks, et vérifiez le corps de la requête brute contre le X-Signature en-tête. Utilisez un paiement test pour des données de commande réelles et les contrôles de simulation d'abonnement de Lemon Squeezy pour les événements du cycle de vie qui sont difficiles à attendre.

À quoi ressemble une configuration locale correcte

Lemon Squeezy envoie un HTTP POST depuis son infrastructure, donc il ne peut pas atteindre localhost sur votre ordinateur. Le tunnel termine le TLS public et transmet la méthode, le corps et les en-têtes inchangés à une route locale telle que http://localhost:3000/api/webhooks/lemonsqueezy.

Un test complet comporte quatre parties : le mode de test Lemon Squeezy, une URL de rappel publique, le même secret de signature dans le tableau de bord et l'environnement local, et un gestionnaire qui vérifie avant de modifier l'état de l'abonnement ou de la licence. Cela est plus utile que de publier du JSON copié avec curl, parce qu'une livraison réelle teste à la fois le routage et la signature générée par le fournisseur.

Créez le webhook et envoyez un événement de test

  1. Exécutez votre application sur le port 3000 et vérifiez que la route est acceptée POST.
  2. Commencez le tunnel avec npx portpreview 3000.
  3. Basculez le tableau de bord Lemon Squeezy en mode Test.
  4. Ouvrez Paramètres → Webhooks et ajoutez https://your-subdomain.portpreview.dev/api/webhooks/lemonsqueezy.
  5. Générez un secret de signature aléatoire et solide, enregistrez-le comme LEMONSQUEEZY_WEBHOOK_SECRET, et sélectionnez uniquement les événements que votre application utilise.
  6. Créez un achat test ou utilisez l’action de simulation d’abonnement en mode Test disponible, puis inspectez le journal de livraison et votre réponse locale.

L'officiel Documentation du webhook Lemon Squeezy vous permet d'inspecter les charges utiles récentes et de renvoyer les webhooks enregistrés depuis la page des paramètres. Un renvoi est précieux pour les tests de régression, mais votre application doit le traiter comme un duplicata plutôt que comme un second événement commercial.

Vérifier X-Signature avant d'analyser le JSON

Lemon Squeezy calcule un digest HMAC-SHA256 sur la charge utile de la requête en utilisant le secret de signature et envoie le digest hexadécimal dans X-Signature. Calculez le condensé sur les octets bruts exacts du corps et comparez des tampons de longueur égale avec une fonction à temps constant. Le principal documentation des demandes de signature comprend des exemples pour Node, PHP, Python et 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 });
}

Ne pas appeler request.json() d'abord. La re-sérialisation de l'objet résultant peut modifier des octets apparemment insignifiants et casser le HMAC. La vérification de la longueur est également essentielle car celle de Node timingSafeEqual lance une exception lorsque les longueurs des tampons diffèrent. Officiel de Lemon Squeezy Tutoriel sur les webhooks Next.js suit la même séquence de corps brut.

Comprenez la charge utile avant de mettre à jour votre base de données

Le corps est un objet ressource JSON:API. meta.event_name identifie l'événement, meta.custom_data transporte les données transmises lors du passage en caisse, et data contient la commande, l'abonnement ou la ressource de clé de licence. Lemon Squeezy envoie également X-Event-Name; traiter le corps signé comme l'entrée faisant autorité et utiliser l'en-tête pour le diagnostic du routage.

Commandes et identité du client

Pour order_created, associez l'achat à un utilisateur interne en utilisant un identifiant généré par le serveur dans les données personnalisées du paiement. Ne faites pas confiance à un identifiant utilisateur fourni par le client sans vérifier la propriété. Conservez l'ID de ressource et l'ID de variante de Lemon Squeezy afin que les événements ultérieurs d'abonnement ou de remboursement puissent être rapprochés.

Les abonnements sont une machine à états

Poignée subscription_created, subscription_updated, subscription_cancelled, subscription_resumed, et subscription_expired comme des transitions distinctes. L'annulation peut signifier que l'accès reste valide jusqu'à la fin de la période de facturation ; l'expiration est le signal plus fort que l'abonnement est terminé. Modélisez les dates et le statut plutôt que de basculer un seul booléen.

Échecs de paiement et récupération

Test a échoué et a récupéré les flux de renouvellement là où le mode Test les prend en charge. Un paiement échoué ne devrait pas nécessairement révoquer l'accès immédiatement ; appliquez la période de grâce définie par votre politique produit et laissez un paiement ultérieur ou des événements d'abonnement faire converger le compte vers l'état le plus récent du fournisseur.

Les nouvelles tentatives nécessitent un traitement idempotent

Selon Lemon Squeezy référence de requête webhook, un 200 les marques de réponse capturent comme réussi. Les autres statuts sont réessayés jusqu'à trois fois de plus avec des délais exponentiels indiqués comme environ 5, 25 et 125 secondes. La réexpédition du tableau de bord crée une autre raison pour laquelle le même événement logique peut arriver plus d'une fois.

Construisez une clé d'idempotence à partir des champs de charge utile signés stables. Une approche pratique consiste à créer une ligne de base de données unique pour l'ID de ressource du webhook, le nom de l'événement et le timestamp de l'événement, ou un hachage du corps signé brut lorsqu'aucun ID d'événement dédié n'est présent. Insérez ce reçu et mettez à jour l'état de l'application dans une seule transaction. Retournez 200 pour un doublon précédemment complété.

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;

Ne faites pas de la livraison d'e-mails ou d'un appel API tiers lent une partie du chemin de reconnaissance. Stockez un travail dans la même transaction, répondez, et laissez un travailleur gérer les effets secondaires. Le plus large guide de relance de webhook et d'idempotence couvre ce modèle.

Dépannage des tests de webhook Lemon Squeezy en localhost

Aucune requête n'atteint l'application

Vérifiez que le tunnel fonctionne toujours, que le rappel contient le suffixe de route et que votre framework a un gestionnaire POST à ce chemin exact. Appelez vous-même le point de terminaison public. Un 502 du tunnel indique le port local ; un 404 du framework indique l'emplacement de la route.

Chaque signature est invalide

Vérifiez que le mode Test et le mode live n'utilisent pas des enregistrements ou des secrets webhook différents. Lisez le corps une fois en tant que texte ou octets, ne tronquez ni le corps ni le secret, et assurez-vous que le middleware JSON global ne s'exécute pas en premier. Inspectez si X-Signature arrivé, mais n'imprimez jamais le secret de signature.

La livraison continue de réessayer

Lemon Squeezy s'attend à 200, pas une redirection vers une page de connexion. Excluez le chemin du webhook des middleware CSRF et de session utilisateur tout en conservant la vérification de la signature. Interceptez les erreurs de base de données, gardez le travail bref, et assurez-vous qu'une réponse de succès soit réellement renvoyée pour chaque branche d'événement traitée.

La simulation d'abonnement n'est pas disponible

Les commandes de simulation nécessitent des données d'abonnement au mode Test appropriées. Certaines simulations de paiement nécessitent un historique de renouvellement. Créez d'abord l'abonnement de test ou l'achat de test requis, puis utilisez le menu d'action d'abonnement décrit dans Lemon Squeezy. guide du développeur webhook.

Liste de contrôle de sécurité et de mise en service

  • Générez un secret de signature aléatoire, stockez-le dans la configuration de l'environnement, et changez-le s'il est exposé.
  • Vérifiez le HMAC sur le corps brut avant l'analyse JSON, les écritures dans la base de données, la génération de licence ou les changements d'accès.
  • Utilisez une comparaison sécurisée contre le chronométrage et rejetez les signatures manquantes ou mal formées.
  • Gardez les secrets et enregistrements du mode Test et des webhooks en direct séparés.
  • Autoriser uniquement POST, appliquer une limite de taille de corps raisonnable, limiter le taux des requêtes invalides et supprimer les données clients des journaux.
  • Testez les doublons, les événements du cycle de vie hors ordre, les interruptions de la base de données et la récupération après expiration avant de remplacer le tunnel par votre URL de production.

Un passage de caisse réussi ne prouve qu'un seul événement arrivé. Une intégration prête pour la production prouve le rejet de signature, la sécurité contre les doublons, la convergence du cycle de vie et la récupération après une réponse non 200. Pour les écueils cryptographiques sous-jacents, voir le guide de vérification de signature.

Questions fréquentes

Comment tester les webhooks Lemon Squeezy sur localhost ?
Exposez votre route de webhook locale avec un tunnel HTTPS, créez un webhook pendant que le tableau de bord est en mode Test, utilisez le même secret de signature localement et déclenchez un test de paiement ou une simulation d'abonnement prise en charge.
Comment une signature de webhook Lemon Squeezy est-elle vérifiée ?
Calculez un digest HMAC-SHA256 sur le corps de la requête brut exact avec votre secret de signature, décodez la valeur hexadécimale X-Signature, et comparez des tampons de longueur égale avec une fonction sécurisée contre les attaques par minutage.
Est-ce que Lemon Squeezy réessaie les webhooks échoués ?
Oui. Sa documentation indique que les réponses autres que 200 sont réessayées jusqu'à trois fois supplémentaires avec un repli exponentiel, approximativement après 5, 25 et 125 secondes.
Puis-je simuler les renouvellements d'abonnement Lemon Squeezy en mode Test ?
Le mode test fournit des actions de simulation pour les événements d'abonnement pris en charge. Certaines simulations liées au paiement nécessitent un abonnement test existant et des données de renouvellement, il faut donc créer d'abord l'état de test nécessaire.