Aller au contenu

Recevoir des webhooks en local

Des événements live transmis à localhost, signés et sans tunnel.

Votre localhost n'est pas accessible depuis Internet. Tester un handler de webhook nécessite donc généralement un déploiement ou un tunnel. wajub listen ne fait ni l'un ni l'autre. Il ouvre un flux depuis votre machine vers Wajub, puis renvoie les événements reçus vers l'URL locale choisie.

Toute la configuration
wajub listen --forward-to localhost:3000/webhooks/wajub

Aucun de vos services n'est exposé. Il n'existe ni URL publique, ni proxy tiers, ni port entrant.

Ce que vous voyez

wajub listen
$ wajub listen --forward-to localhost:3000/webhooks/wajub

Ready! Streaming sandbox events for your account.
Your webhook signing secret is whsec_test_c7f543a6a4058db0dfb566dd91e922bc (^C to quit)

6:37:35 AM  customer.created    evt_test_NGHXbVLyHqPLOhNY3HKoXMca 200 http://localhost:3000/webhooks/wajub
6:37:35 AM  payment.created     evt_test_wUpoY1JcMvjoUEs8FUdBkbuw 200 http://localhost:3000/webhooks/wajub
6:37:36 AM  payment.processing  evt_test_7wP9Ty2ITqLSzVgWaU1VFH8U 200 http://localhost:3000/webhooks/wajub
6:37:39 AM  payment.succeeded   evt_test_HObNwAnDRT54qb28OxW7fwQJ 200 http://localhost:3000/webhooks/wajub
6:37:39 AM  balance.updated     evt_test_8pymkrpN71IQnazTsIwSxEVB 200 http://localhost:3000/webhooks/wajub
6:37:39 AM  fee.charged         evt_test_u5zOJ37yKP2C0HSQZAKbUtme 200 http://localhost:3000/webhooks/wajub

Le nombre à droite est la réponse de votre propre endpoint. Un 500 indique donc l'échec de votre handler, pas celui de la CLI. L'exécution ci-dessus correspond à un seul wajub trigger payment.succeeded. Une commande a produit six événements, comme un véritable paiement.

C'est le moyen le plus rapide de découvrir le nombre de types silencieusement ignorés par votre handler.

La requête transmise est un véritable webhook

La CLI signe les événements transmis avec le même mécanisme que la production et envoie les mêmes en-têtes.

En-têteValeur
X-Wajub-Signaturev1= suivi du HMAC
X-Wajub-TimestampSecondes Unix, incluses dans la chaîne signée
X-Wajub-EventLe type d'événement
X-Wajub-Delivery-IdCette tentative, whd_ suivi d'un UUID

Votre code de vérification s'exécute donc réellement en local, au lieu d'être ignoré derrière un if (process.env.NODE_ENV === 'development') qui masque un défaut jusqu'à la production.

Contenu reçu par votre endpoint
{
"id": "evt_test_wUpoY1JcMvjoUEs8FUdBkbuw",
"event": "payment.created",
"data": {
"id": "trx_test_VPRKcDWn0pi29ScaQL9B",
"amount": 5000,
"status": "pending",
"currency": "XAF",
"sandbox": true,
"amount_paid": 0,
"description": "Payment",
"customer": {
"id": "cus_test_abOtZa54yWAc6xihrXrU2F0C",
"name": "CLI Trigger",
"email": "cli-trigger@example.com"
},
"created_at": "2026-09-14T04:37:34+00:00",
"updated_at": "2026-09-14T04:37:34+00:00"
},
"livemode": false,
"pending_webhooks": 0,
"api_version": "2026-09-01",
"request": {
"id": null,
"idempotency_key": "idem_4CNn7YQyA1OgyNJy53YT5wTosFAYyWyn"
},
"created": "2026-09-14T04:37:34+00:00"
}

Deux champs diffèrent d'une livraison en production. pending_webhooks vaut 0, car le flux n'est pas un endpoint enregistré. Le User-Agent est wajub-cli au lieu de Halo/1.0. Un handler qui adapte son comportement selon l'un de ces champs fonctionnera différemment en local et après son déploiement. Supprimez ce piège au lieu de le contourner.

Le secret affiché au démarrage est généré pour cette exécution. Exportez-le pour que votre handler effectue la vérification sans autre modification.

Configurer votre application avec le secret affiché
export WAJUB_WEBHOOK_SECRET=whsec_test_c7f543a6a4058db0dfb566dd91e922bc
npm run dev

Un nouveau secret à chaque exécution devient fastidieux. Fixez-en un et placez-le une seule fois dans votre .env.

Un secret local stable
wajub listen --forward-to localhost:3000/webhooks/wajub --secret whsec_local_dev

Options

OptionEffet
--forward-toDestination du POST. http:// est utilisé lorsque le schéma est absent
--eventsTypes séparés par des virgules, à la place de tous les événements
--secretUtilise ce secret de signature au lieu d'en générer un
--skip-verifyTransmet sans signature. Consultez l'avertissement ci-dessus
--profileCompte et environnement dont le flux est utilisé
Limiter le flux
# Only what your handler currently implements
wajub listen \
  --forward-to localhost:3000/webhooks/wajub \
  --events payment.succeeded,payment.failed,refund.succeeded

# Watch without forwarding anywhere
wajub listen

Sans --forward-to, la commande affiche les événements sans les envoyer. Utilisez-la pour vérifier si un événement est émis avant d'accuser votre handler.

Quatre éléments à connaître

L'environnement provient de votre profil. Une clé de sandbox diffuse les événements de la sandbox et une clé live les événements live. wajub doctor indique le profil utilisé. --profile le remplace pour une exécution.

Aucun endpoint enregistré n'est nécessaire. Le flux est indépendant de POST /webhooks. Même un compte sans endpoint transmet tous ses événements à wajub listen.

Il ne remplace pas vos endpoints enregistrés. Un endpoint live abonné aux mêmes événements continue de les recevoir. L'écoute ne détourne rien.

Aucune donnée n'est mise en mémoire tampon. Les événements produits lorsque la commande est arrêtée ne sont pas rejoués à son démarrage. Pour les récupérer, consultez wajub events list et renvoyez ceux qui vous intéressent.

Les événements internes n'apparaissent jamais

Les événements transaction.* sont des données de télémétrie internes au checkout. Ils sont filtrés avant le flux, n'atteignent jamais wajub listen et ne peuvent pas être renvoyés.

La boucle

Deux terminaux suffisent, sans toucher à un téléphone.

Terminal 1, puis terminal 2
# 1: forward everything to your handler
wajub listen --forward-to localhost:3000/webhooks/wajub

# 2: make something happen
wajub trigger payment.succeeded
wajub trigger payment.failed --amount 2500

Écrivez le handler, enregistrez, puis déclenchez de nouveau l'événement. La boucle dure quelques secondes, ce qui est précisément son intérêt.

Que pensez-vous de ce contenu ?