Erreurs et limites
Le code est `status`, jamais `code`. Quotas, taille de corps, support.
La forme d'une erreur
{
"success": false,
"status": "AMOUNT_OUT_OF_RANGE",
"message": "Montant hors limites",
"data": { "minAmount": "100", "maxAmount": "1000000", "providedAmount": "50" }
}Le code machine-lisible est status. Il n'y a pas de champ code, et il
n'y a pas d'objet error imbriqué. Une intégration qui lit error.code lit
undefined sur toutes les erreurs.
errors n'est présent que sur les erreurs de validation de schéma. data n'est
présent que là où l'erreur transporte quelque chose d'utile : { orderId, status } sur un 409 d'orderId déjà pris, { workflow } sur un 422,
{ minAmount, maxAmount, providedAmount } (en plus petite unité, donc string
plutôt qu'un number qui perdrait la précision) sur un 400 AMOUNT_OUT_OF_RANGE — minAmount/maxAmount couvrent l'enveloppe des
corridors candidats pour ce couple numéro/devise, pas une garantie qu'un
montant dans cette plage aurait été accepté : deux corridors peuvent couvrir
des tranches disjointes. maxAmount est absent dès qu'un candidat n'a pas de
plafond. Le 409 d'un renvoi de webhook hors statut terminal, lui, n'en porte
pas.
message n'est pas contractuel
Les messages sont rédigés en français, et Accept-Language n'est pas pris
en compte : il n'existe pas de version anglaise. Leur formulation peut changer
sans préavis.
Ne faites jamais de correspondance sur le texte de message. Branchez-vous sur
status, et affichez à vos utilisateurs vos propres libellés.
Ceci concerne les erreurs API (4xx/5xx). Pour l'échec d'une transaction
(status: "failed" ou "cancelled"), voir Causes d'échec :
même principe, failureCategory est la valeur normalisée, failureReason et
failureCode le détail brut non contractuel.
Sur toute erreur de code HTTP 500 ou plus, message est remplacé par
« Une erreur est survenue » : ces messages-là sont écrits pour l'exploitant az54
et ne vous sont pas destinés. Le status reste, lui, informatif.
Table des status
| HTTP | status | Quand |
|---|---|---|
| 400 | VALIDATION_ERROR | Corps non conforme au schéma, ou montant plus précis que la devise. |
| 400 | BAD_REQUEST | Requête recevable par le schéma mais inexploitable (opérateur indétectable, par exemple). |
| 400 | INVALID_BODY | Corps illisible (JSON malformé). |
| 400 | AMOUNT_OUT_OF_RANGE | Montant hors des bornes du corridor. |
| 400 | CORRIDOR_NOT_FOUND | Aucun corridor actif pour ce couple numéro / devise. |
| 401 | UNAUTHORIZED | Clé absente, malformée ou révoquée. |
| 403 | <PORTÉE>_NOT_ALLOWED | Clé d'API sans la portée requise (PAYOUT_INITIATE_NOT_ALLOWED, par exemple). |
| 403 | FORBIDDEN | Rôle insuffisant, ou permission non attribuable à une clé d'API. |
| 403 | 2FA_REQUIRED | Second facteur requis. Session du tableau de bord seulement : une clé n'y est pas soumise. |
| 404 | NOT_FOUND | Ressource inexistante, ou orderId ambigu sur le reçu public. |
| 409 | CONFLICT | Cet orderId existe déjà pour votre organisation, ou renvoi de webhook hors statut terminal. |
| 413 | PAYLOAD_TOO_LARGE | Corps au-delà de 10 ko. |
| 415 | UNSUPPORTED_MEDIA_TYPE | Content-Type non supporté. |
| 422 | OTP_REQUIRED | Le corridor payant exige un OTP absent. |
| 422 | REDIRECT_URLS_REQUIRED | Le corridor payant exige des redirectUrls absentes. |
| 429 | TOO_MANY_REQUESTS | Quota dépassé. |
| 502 | <PSP>_ERROR | Le prestataire a échoué ou n'a pas répondu. |
La dernière ligne n'est pas une valeur fixe : le status est construit à
l'exécution à partir du code du prestataire qui a échoué. Traitez tout 502
par le suffixe _ERROR, ou plus simplement par son code HTTP.
<PORTÉE>_NOT_ALLOWED n'est pas non plus une valeur fixe : c'est le nom de la
portée manquante suivi de _NOT_ALLOWED (PAYIN_INITIATE, PAYOUT_INITIATE,
PAYOUT_APPROVE, TRANSACTIONS_VIEW, WEBHOOKS_MANAGE, et la liste peut s'allonger). Routez sur le suffixe
_NOT_ALLOWED : la clé n'a pas ce droit, et la corriger passe par une clé
créée avec la bonne portée (Authentification).
Un 502 ne signifie pas « échec ». Il signifie « issue indéterminée » :
voir Idempotence.
Quotas
L'API générale accepte 1000 requêtes par tranche de 15 minutes, comptées par clé d'API. Le quota courant est lisible sur chaque réponse :
RateLimit-Limit: 1000
RateLimit-Remaining: 997
RateLimit-Reset: 812Deux plafonds s'ajoutent à celui-là, sur leurs endpoints seulement :
- 3 par minute et par transaction sur
POST /v1/transactions/by-id/{transactionId}/resend-callback(Réconciliation) ; - ceux du reçu public, nettement plus stricts, qui lui sont propres.
Taille de corps
Les corps de requête sont plafonnés à 10 ko. Au-delà, 413 PAYLOAD_TOO_LARGE. C'est très largement suffisant pour un pay-in ; si vous
approchez cette limite, c'est probablement que vous essayez d'envoyer des
données qu'aucun champ du schéma n'accepte.
Support
Envoyez un en-tête x-correlation-id sur vos requêtes : il est repris tel quel
et renvoyé dans la réponse. En son absence, az54 en génère un — il est de toute
façon présent dans les en-têtes de réponse.
C'est la référence à citer au support, avec le transactionId s'il y en a un.
Ni orderId seul, ni l'horodatage ne permettent de retrouver une requête aussi
sûrement.
