Aller au contenu

Recevoir des webhooks en local

Des événements live transmis à localhost, signés par Wajub 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é : ni URL publique, ni port entrant. La CLI récupère les données de chaque événement auprès de l'API Wajub avec votre clé. Ces données ne transitent donc jamais par la connexion temps réel, et rien n'est envoyé tant qu'aucun wajub listen ne tourne.

CLI 1.6.1 ou plus récente

Les versions antérieures de la CLI s'arrêtent avec un message qui demande la mise à jour : npm install -g @wajub/cli@latest. Toutes les autres commandes continuent d'y fonctionner.

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_xxxxxxxxxxxxxxxx… (^C to quit)
It stays the same between runs: set WAJUB_WEBHOOK_SECRET=whsec_test_xxxxxxxxxxxxxxxx… in your app's .env once.
Forwarding to http://localhost:3000/webhooks/wajub
subscribed — waiting for events…

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

Wajub construit et signe chaque livraison locale comme une livraison de production, et la CLI la relaie à l'octet près : le corps JSON encodé comme en production, échappements compris, avec les mêmes en-têtes. Seul pending_webhooks peut différer : il compte les livraisons encore en attente au moment où la CLI récupère l'événement, alors qu'une livraison de production porte le compte du moment de son envoi.

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-Idwhd_ suivi d'un UUID, identique pour chaque livraison d'un événement à cette écoute
User-AgentWajub-Webhooks/1.0, comme en production

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-10-11",
"request": {
"id": null,
"idempotency_key": "idem_4CNn7YQyA1OgyNJy53YT5wTosFAYyWyn"
},
"created": "2026-09-14T04:37:34+00:00"
}

Comme le corps est celui de la production, un handler qui vérifie la signature sur le corps brut réussit en local exactement quand il réussira une fois déployé. Les CLI antérieures à la 1.6.1 réencodaient le JSON et le signaient elles-mêmes : \/ et les caractères accentués pouvaient alors différer.

Le secret de signature reste le même à chaque wajub listen pour ce compte et cet environnement : un pour la sandbox, un pour le live. Placez-le une seule fois dans votre .env et votre handler effectue la vérification sans autre modification. Ce n'est pas le secret d'un endpoint enregistré : chaque endpoint garde le sien.

Configurer votre application avec le secret d'écoute
export WAJUB_WEBHOOK_SECRET=$(wajub listen --print-secret)
npm run dev

--print-secret affiche le secret seul puis s'arrête, ce qui le rend utilisable dans un script. Avec une clé live, ajoutez --live (wajub listen --live --print-secret) : sans lui la commande échoue, et $(…) récupère alors un secret vide.

Si votre application répond sur une adresse https:// avec un certificat auto-signé ou émis par une autorité locale, comme un site local avec un certificat auto-signé, ajoutez --insecure-tls. La vérification du certificat est ignorée uniquement pour la transmission à votre handler ; les appels de la CLI à l'API Wajub restent vérifiés. Faire pointer NODE_EXTRA_CA_CERTS vers votre autorité locale fonctionne aussi.

Options

OptionEffet
--forward-toDestination du POST. http:// est utilisé lorsque le schéma est absent
--eventsTypes séparés par des virgules, jokers acceptés (payment.*), à la place de tous les événements
--liveObligatoire avec une clé live : les événements de vrais clients arrivent sur cette machine
--print-secretAffiche le secret de signature puis s'arrête
--unsignedTransmet sans signature. Consultez l'avertissement ci-dessus
--insecure-tlsAccepte un certificat auto-signé ou d'une autorité locale sur une URL https:// de transmission
--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.*,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. Une valeur --events mal formée arrête la commande avant tout démarrage, et Wajub n'envoie que les types demandés.

À connaître

L'environnement provient de votre profil. Une clé de sandbox diffuse les événements de la sandbox. Une clé live diffuse les événements live, et seulement avec --live : les données de vrais clients arrivent alors sur votre machine. wajub doctor indique le profil utilisé. --profile le remplace pour une exécution.

Il faut une clé secrète, ou une clé restreinte avec event.read. La clé créée par wajub login a un accès complet, sauf si vous la restreignez. Une clé publique ne peut pas écouter, et une clé révoquée par wajub logout arrête un flux en cours en moins d'une demi-minute.

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.

Vos écoutes apparaissent dans le Dashboard. Konsole › Webhooks liste chaque wajub listen en cours pour l'environnement actuel, avec la réponse de votre handler aux derniers événements.

Il survit à la mise en veille. Une connexion morte sans prévenir est remplacée en deux minutes et demie au plus, et une session expirée pendant la veille de votre ordinateur est rouverte toute seule.

Vingt à la fois. Un compte peut avoir 20 sessions wajub listen ouvertes par environnement. La vingt et unième répond 429 : la CLI attend les 30 secondes que demande son Retry-After et réessaie, autant de fois que WAJUB_MAX_RETRIES le permet (deux par défaut), puis s'arrête en le disant. Arrêter une commande avec ^C libère sa place aussitôt ; un terminal fermé sans ^C la libère en 90 secondes au plus.

Un 503 signifie que le temps réel est indisponible. La plateforme n'a pas pu ouvrir le flux : la commande s'arrête avant de commencer. Réessayez dans un instant ; si cela persiste, transmettez au support l'identifiant de requête affiché par la CLI. Toutes les autres commandes continuent de fonctionner.

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 que Wajub enregistre pour son propre usage ne sont jamais livrés : ils 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 ?