Aller au contenu

Webhooks ou polling

Pourquoi les webhooks l'emportent, quand lire l'API à la place, et ce que cela coûte.

Un paiement Mobile Money n'est pas terminé quand votre requête répond. Le payeur doit encore ouvrir l'invite USSD et saisir son code PIN, ce qui prend de quatre secondes à quatre minutes. Il n'existe donc que trois moyens de savoir comment il s'est terminé.

MoyenMécanismeLatenceCe que cela coûte
WebhookWajub envoie un POST à votre URLMoins d'une secondeUn endpoint HTTPS public
Lire un paiementGET /payments/{id}Votre intervalle de pollingDu budget de limite de requêtes
Lire le journal des événementsGET /eventsÀ chaque exécutionUne requête par page

Pour livrer la commande, la réponse, ce sont les webhooks. Les deux autres existent pour les cas que les webhooks ne couvrent vraiment pas, et cette page vous aide à les distinguer.

Utilisez chacun pour ce qu'il fait bien

SituationÀ utiliser
Livrer la commande, créditer le portefeuille, envoyer le reçuWebhook, toujours
Le payeur vient de revenir sur votre page de retourUne lecture de GET /payments/{id}
Un script, un cron, un outil de back-office sans URL publiquePolling, avec une échéance
Votre endpoint a été indisponible une heure et vous devez combler le trouGET /events, puis rejouer
Un tableau de bord qui affiche le statut en direct à une personneWebhook vers votre propre canal push

Le polling n'est pas une alternative plus légère aux webhooks. Il est plus lent, il consomme du budget de requêtes, et il ne voit rien de ce qui s'est passé pendant que votre processus ne tournait pas. C'est un complément.

Ce que le polling vous coûte réellement

GET /payments/{id} est relu en base de données à chaque appel, un poll vous donne donc toujours le statut actuel. Il est aussi compté deux fois : une fois sur le budget global par minute de votre compte, et une fois sur le compartiment de l'endpoint payments, partagé avec la création de paiements.

PlanBudget du compte, par minuteCompartiment payments, par minute
Pay as you go12060
Growth360180
Scale72030
EnterpriseIllimité30

Au-delà du plafond, vous recevez un 429 avec un en-tête Retry-After en secondes, et la même valeur dans le corps.

Un 429 du compartiment payments
{
"code": 429,
"status": "Too Many Requests",
"message": "Too many requests",
"type": "endpoint",
"retry_after": 37,
"retry_after_human": "00:00:37"
}

type vous indique quel plafond vous avez atteint : team pour le budget du compte, endpoint pour le compartiment ci-dessus, api_key pour la limite par clé, et ip.

Faire du polling correctement

Quatre règles font la différence entre un polling utile et un polling qui vous fait atteindre la limite de requêtes.

RèglePourquoi
Arrêtez-vous à un statut terminalsucceeded, failed, cancelled et expired sont définitifs. pending, processing et partial ne le sont pas
Donnez une échéance à la boucleDeux minutes suffisent pour le Mobile Money. Au-delà, laissez le webhook terminer le travail et indiquez au client que vous confirmerez par e-mail
Appliquez une attente progressiveCommencez à deux secondes et augmentez l'intervalle. Un paiement qui n'a pas bougé en trente secondes ne bougera pas dans les deux suivantes
Ne relisez jamais ce qu'on vous a déjà transmisUne livraison payment.succeeded contient déjà le paiement final. Le relire double votre trafic pour rien
# One read. The status is in .transaction.status
curl https://api.wajub.com/payments/trx_CSUGajfv9xh0XQ5wu2lx \
-H "Authorization: sk.kZ3qP8mWvL2xR7tB5nY4hC6dF9jS1aG0eU3i…"

Les quatre SDKs déballent l'enveloppe de la réponse : retrieve vous renvoie directement le paiement plutôt que le conteneur { code, status, message, transaction } que vous voyez avec cURL.

Rattraper le retard après une panne

Si votre endpoint était injoignable, les événements existent toujours. GET /events liste tout ce qui a été enregistré pour votre compte dans l'environnement courant, du plus récent au plus ancien, et POST /events/{id}/resend en renvoie un par le circuit de livraison normal, signé comme d'habitude.

curl "https://api.wajub.com/events?type=payment.succeeded&per_page=100" \
-H "Authorization: sk.kZ3qP8mWvL2xR7tB5nY4hC6dF9jS1aG0eU3i…"

curl -X POST https://api.wajub.com/events/evt_aio5DpN577tNU2vOxdmuZGhT/resend \
-H "Authorization: sk.kZ3qP8mWvL2xR7tB5nY4hC6dF9jS1aG0eU3i…"

Trois détails déterminent si cela fonctionne.

DétailCe que cela signifie
Les filtres sont per_page, page et typeIl n'y a pas de plage de dates. Remontez les pages jusqu'à atteindre des horodatages assez anciens
La pagination se fait par numéro de pagemeta contient current_page, last_page, per_page et total
Sandbox et live sont séparésL'environnement dépend de la clé utilisée, pas d'un paramètre

Renvoyer un événement n'est pas sans conséquence : l'événement part vers tous les endpoints actifs abonnés à ce type, pas seulement vers celui qui l'a manqué. Sur un compte avec deux endpoints, celui qui fonctionne reçoit un doublon, une raison de plus pour que le handler soit idempotent.

Les événements `transaction.*` ne peuvent pas être renvoyés

Il s'agit de télémétrie interne du checkout, jamais livrée aux marchands. Une demande de renvoi répond 422 Internal events cannot be resent.

La page de retour n'est pas une confirmation

Quand un payeur revient sur votre URL callback, vous savez seulement qu'un navigateur a chargé une URL. Le paiement a pu réussir, échouer, ou attendre encore un code PIN. N'importe qui peut aussi ouvrir cette URL s'il la devine.

Le schéma qui fonctionne en production repose sur trois couches, et chacune couvre l'angle mort de la précédente.

  1. 1

    Le webhook livre la commande

    Il arrive, que le client revienne ou non, et c'est le seul des trois dont l'arrivée est garantie.

  2. 2

    Une lecture côté serveur affiche la page de retour

    Un seul GET /payments/{id} vous indique laquelle des trois pages afficher : réussi, toujours en attente, ou échoué. Pas de boucle.

  3. 3

    Une tâche quotidienne fait le rapprochement

    Listez les paiements que votre base de données considère encore comme ouverts, lisez chacun d'eux, et comblez l'écart. Cela rattrape l'événement rare qui n'a jamais été livré ni rejoué.

Que pensez-vous de ce contenu ?