Performances
Parcourez correctement les listes, mettez en cache les données stables et cadencez vos appels sous les plafonds.
Optimiser les performances d'une intégration de paiement consiste rarement à gagner quelques millisecondes. Il s'agit surtout de ne pas envoyer mille requêtes lorsque trente suffisent, car l'export nocturne consomme le même quota que le checkout utilisé à midi.
Trois sujets couvrent presque tous les besoins : la façon de parcourir une liste, les données que vous pouvez mettre en cache et le rythme auquel vous envoyez vos appels.
Parcourir une liste
Tous les endpoints de liste sont paginés. La réponse contient un tableau items accompagné d'un
objet meta, sauf GET /events, dont le tableau s'appelle events. Les paramètres sont identiques
partout.
| Paramètre | Type | Valeur par défaut | Remarques |
|---|---|---|---|
per_page | integer | 25 | De 1 à 100, et 100 convient presque toujours |
page | integer | 1 | Mode par décalage |
cursor | string | Mode par curseur, voir Pagination | |
date_from, date_to | YYYY-MM-DD | Limites appliquées à created_at | |
status | string | Un seul statut, pas une liste |
Le mode choisi modifie la structure de meta, ce qui provoque souvent des erreurs.
| Mode | Contenu de meta |
|---|---|
| Décalage | current_page, last_page, per_page, total, next_cursor, has_more |
| Curseur | per_page, next_cursor, prev_cursor, has_more |
Écrire la boucle à la main multiplie les risques : erreur de décalage sur la dernière page, compteur qui n'augmente jamais ou liste qui s'allonge pendant son parcours. Chaque SDK fournit un itérateur qui gère ces cas.
# Offset mode: walk until current_page reaches last_page
curl "https://api.wajub.com/payments?per_page=100&page=1&date_from=2026-09-01&date_to=2026-09-30" \
-H "Authorization: sk_test.xxxxxxxxxxxxxxxx…" \
| jq '{ count: (.items | length), page: .meta.current_page, last: .meta.last_page }'Le SDK renomme le tableau
La liste HTTP arrive dans items. Les SDKs la renvoient dans data, avec le même objet meta.
Extraire items du résultat d'un SDK donne undefined, puis la boucle échoue dès la première
itération.
Pour un export complet, procédez par périodes plutôt que par pages. Une période d'un mois produit des requêtes bornées et reproductibles. Relancer une période n'est pas perturbé par les objets créés depuis. Parcourir profondément les pages d'une liste qui continue de grandir peut afficher deux fois le même élément ou en ignorer un.
Mettez en cache ce qui ne change pas
Trois endpoints décrivent la plateforme plutôt que votre compte. Ce sont ceux qu'il est utile de mettre en cache.
| Ressource | Endpoint | TTL raisonnable |
|---|---|---|
| Canaux de paiement | GET /channels | 1 heure |
| Pays pris en charge | GET /countries | 24 heures |
| Devises prises en charge | GET /currencies | 24 heures |
Ce sont aussi les seules routes accessibles à une clé restreinte sans scope, ce qui montre qu'elles contiennent très peu de données sensibles.
const cache = new Map();
async function cached(key, ttlMs, fetcher) {
const hit = cache.get(key);
if (hit && Date.now() - hit.at < ttlMs) return hit.value;
const value = await fetcher();
cache.set(key, { value, at: Date.now() });
return value;
}
const channels = await cached('channels:CM', 3_600_000, () =>
wajub.global.channels({ country: 'CM' }),
);Ne mettez jamais en cache l'état d'une transaction
Un paiement peut passer de processing à succeeded à tout moment. Un remboursement peut échouer
une heure après sa création. Si vous mettez un statut en cache, vous risquez d'expédier une
commande jamais payée. L'état d'une transaction provient d'un webhook ou d'une nouvelle lecture.
Cette règle a une conséquence simple : n'utilisez pas non plus le polling. Interroger chaque paiement toutes les quelques secondes est le moyen le plus courant d'atteindre une limite de requêtes sans rien apprendre. Le webhook vous informe déjà. Webhooks ou polling présente les rares cas où une lecture reste adaptée.
Cadencez vos appels à la minute
Cent requêtes simultanées dépassent un plafond, et vous recevez cent réponses 429 au lieu de cent
résultats. La parade habituelle est un groupe d'appels : huit en cours, les autres en attente. Elle
borne la mauvaise grandeur. L'API ne compte pas les appels que vous avez en cours, elle compte ceux
qui arrivent en une minute, et huit appels en cours sur une lecture qui répond en 200 ms font
2 400 appels par minute.
Ce qu'il faut régler est un débit, sous le plafond de la ressource appelée, pour votre plan. Ce sont des appels par minute, et chaque appel sur la ressource compte, lectures comprises, quelle que soit la clé qui l'envoie.
| Ressource | Pay as you go | Growth | Scale | Enterprise |
|---|---|---|---|---|
/payments | 30 | 90 | 180 | Aucun plafond |
/transfers | 20 | 60 | 120 | Aucun plafond |
/refunds | 10 | 30 | 60 | Aucun plafond |
/customers | 50 | 150 | 300 | Aucun plafond |
Limites présente ce tableau, avec le plafond de l'équipe qui s'applique au-dessus.
Votre checkout puise dans le même compteur que votre traitement par lots : un lot qui tourne au
plafond ne lui laisse rien. Les quatre cinquièmes du plafond sont une part raisonnable : sur
/payments, 24 appels par minute sur Pay as you go, 72 sur Growth, 144 sur Scale.
import { WajubRateLimitError } from '@wajub/node';
// /payments accepte 30 appels par minute sur Pay as you go : 24 laissent de la place au checkout.
const PER_MINUTE = 24;
const gap = 60_000 / PER_MINUTE;
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const payments = [];
for (const id of paymentIds) {
const startedAt = Date.now();
let payment;
try {
payment = await wajub.payments.retrieve(id);
} catch (err) {
if (!(err instanceof WajubRateLimitError)) throw err;
// Autre chose a consommé la minute : attendez le délai donné par l'API, puis relisez une fois.
await sleep((err.retryAfter ?? 60) * 1000);
payment = await wajub.payments.retrieve(id);
}
payments.push(payment);
await sleep(Math.max(0, gap - (Date.now() - startedAt)));
}Un débit aussi bas demande rarement des appels en parallèle : 24 par minute, c'est un appel toutes
les 2,5 secondes, et 144 par minute sur Scale, c'est encore un appel toutes les 417 ms. Le SDK
Node.js attend déjà le Retry-After d'un 429 et répète une lecture jusqu'à trois fois. Le catch
sert au 429 qui revient malgré tout, le plus souvent parce qu'un autre traitement consomme la
même minute. Gestion des limites de requêtes présente les trois
compteurs et les deux formes d'un 429.
Adaptez les délais d'expiration au parcours critique
Le SDK attend jusqu'à 30 secondes par tentative et relance un appel qui a expiré, jusqu'à trois
fois : une API injoignable se traduit par une erreur au bout d'environ deux minutes. Ce délai convient
à un job en arrière-plan, mais il est beaucoup trop long pour un client face à un indicateur de
chargement. Il rechargera la page et votre handler s'exécutera deux fois. timeout règle l'attente
de chaque tentative.
const payment = await wajub.payments.create(
{ amount: order.total, currency: order.currency, email: order.email },
{ idempotencyKey: `ORDER-${order.id}`, timeout: 8000 },
);Réduire le délai d'expiration augmente le risque d'un résultat ambigu, car vous abandonnez des
requêtes qui étaient sur le point de réussir. C'est précisément pour cette raison que la clé
d'idempotence accompagne le même appel. Sans cette clé, chaque requête lente peut provoquer un
double débit. Renvoyée pendant que le premier appel est encore en cours, la même clé reçoit un 409
avec errors.idempotency_key : les SDK serveur attendent puis redemandent, et vous obtenez le
résultat du premier appel au lieu d'une erreur.
Mesures à suivre
Quatre mesures indiquent si l'intégration fonctionne correctement. Aucune n'est une moyenne.
| Signal | À surveiller | Action |
|---|---|---|
| Latence de l'API | Le p95, pas la moyenne | La moyenne masque les requêtes qui expirent |
Taux de 429 | Toute valeur durablement supérieure à zéro | Une boucle existe, trouvez-la avant qu'elle ne s'aggrave |
Taux de 5xx | Une hausse, pas un pic isolé | Un cas est du bruit, une tendance indique un incident |
| Retard des webhooks | De l'horodatage de l'événement à son traitement | Un écart croissant signifie que votre file prend du retard |
Konsole fournit une vue par requête pour un paiement donné. Surveillance explique comment relier ces mesures à votre propre système d'alerte.
Pages associées