Toutes les erreurs SandPay suivent un format unique et stable. Pas de surprise, pas de format ad hoc selon l’endpoint.
{
"error": "invalid_body",
"message": "amount must be greater than 0",
"detail": {
"fieldErrors": {
"amount": ["Number must be greater than 0"]
}
}
}
error — code machine, stable, à utiliser dans votre logique (switch, if).
message — texte humain, en anglais, susceptible d’évoluer.
detail — payload structuré optionnel (erreurs Zod par champ, contexte additionnel).
L’en-tête X-Request-Id accompagne chaque réponse (succès comme erreur). Conservez-le si vous ouvrez un ticket support.
Codes par statut HTTP
| Status | error | Quand |
|---|
| 400 | invalid_body | Le corps JSON ne valide pas le schéma (Zod). detail contient les champs en faute. |
| 400 | invalid_params | Paramètres de query invalides (par ex. limit=999). |
| 401 | unauthorized | En-tête Authorization absent. |
| 401 | invalid_api_key | Clé inconnue, révoquée ou mal formée. |
| 402 | quota_exceeded | Quota mensuel atteint sur le plan courant. Mettez à niveau. |
| 404 | not_found | Ressource introuvable (transaction, application, client…). |
| 404 | env_not_found | Le couple country/operator n’a pas d’environnement configuré. |
| 429 | rate_limited | Trop de requêtes — retry après l’en-tête Retry-After. |
| 5xx | internal | Erreur côté SandPay. Tracée dans Sentry, retry recommandé. |
Stratégie de retry
| Status | Retriable ? | Comment |
|---|
| 4xx (sauf 429) | Non | Corrigez la requête. Réessayer ne changera rien. |
| 429 | Oui | Respectez Retry-After, puis backoff exponentiel. |
| 5xx | Oui | Backoff exponentiel : 1s, 2s, 4s, 8s, 16s. Max 5 essais. |
Le SDK Node lève une SandPayApiError qui expose status, code, message, detail et requestId — utilisez-la pour brancher votre logique de retry.
Exemple — gérer un quota dépassé
import { SandPay, SandPayApiError } from "@sandpay/node";
const sp = new SandPay({ apiKey: process.env.SANDPAY_API_KEY! });
try {
await sp.payments.create({ /* ... */ });
} catch (err) {
if (err instanceof SandPayApiError && err.code === "quota_exceeded") {
// Notifier l'équipe, déclencher l'upgrade, etc.
notifyOps("SandPay quota dépassé, requestId=" + err.requestId);
return;
}
throw err;
}