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} renvoie 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 go12030
Growth36090
Scale720180
EnterpriseIllimitéIllimité

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é, qui a la même valeur que le budget du compte, et ip pour un appel fait sans clé.

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.xxxxxxxxxxxxxxxx…"

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.xxxxxxxxxxxxxxxx…"

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

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

Le renvoi part vers chaque endpoint actif abonné à ce type qui n'a pas encore accepté l'événement, chacun recevant sa tentative suivante. Un endpoint qui l'a déjà accepté ne le reçoit pas une seconde fois : sur un compte avec deux endpoints, celui qui fonctionne ne reçoit pas de doublon. Quand tous l'ont accepté, l'appel répond quand même 202 et n'envoie rien.

Le journal ne contient que ce que reçoit un endpoint

Les entrées de la chronologie du checkout enregistrées à côté des événements (transaction.*, payment.attempt_refused, payment.checkout_page_opened, payment.redirected_to_callback) ne sont jamais livrées : GET /events ne les liste donc pas et répond 404 pour l'une d'elles demandée par son id, en lecture comme en renvoi. Elles restent visibles dans Événements dans Konsole.

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. Cette étape est obligatoire. Un événement peut vous parvenir jusqu'à une heure en retard pendant que Wajub le reconstruit, et aucun n'est envoyé tant que Wajub restreint vos webhooks (Nouvelles tentatives) : sans cette tâche, un tel paiement reste ouvert chez vous pour de bon.

Que pensez-vous de ce contenu ?