Documentation Index Fetch the complete documentation index at: https://docs.oneclickdz.com/llms.txt
Use this file to discover all available pages before exploring further.
Structure de la Réponse d’Erreur
Toutes les erreurs suivent un format cohérent :
{
"success" : false ,
"error" : {
"code" : "ERROR_CODE" ,
"message" : "Human-readable error message" ,
"details" : {
// additional context
}
},
"requestId" : "req_abc123"
}
Codes de Statut HTTP
Statut Catégorie Description 400Erreur Client Paramètres de requête invalides ou erreur de validation 401Authentification Clé API invalide ou manquante 403Autorisation Permissions ou solde insuffisants 404Non Trouvé La ressource n’existe pas 429Limite de Taux Trop de requêtes 500Erreur Serveur Erreur interne du serveur 503Indisponible Service temporairement indisponible
Codes d’Erreur Courants
Erreurs d'Authentification (401)
MISSING_ACCESS_TOKEN
Message : Access token is required
Cause : L’en-tête X-Access-Token est manquant
Action : Incluez votre clé API dans l’en-tête
// ✅ Correct
fetch ( url , {
headers: { 'X-Access-Token' : 'YOUR_API_KEY' }
})
INVALID_ACCESS_TOKEN / ERR_AUTH
Message : The provided access token is invalid
Cause : La clé API est incorrecte, expirée ou révoquée
Action :
Vérifiez que la clé API est correcte
Générez une nouvelle clé si nécessaire
Ne journalisez pas les valeurs de clé sensibles
Contactez le support si le problème persiste
Erreurs d'Autorisation (403)
NO_BALANCE / INSUFFICIENT_BALANCE
Message : Insufficient balance
Cause : Le solde du compte est trop faible
Action :
Vérifiez le solde via /v3/account/balance avant les opérations
Affichez le solde actuel à l’utilisateur
Proposez une option de rechargement
N’effectuez pas de nouvelle tentative sans avoir ajouté des fonds
const balanceRes = await fetch ( 'https://api.oneclickdz.com/v3/account/balance' , {
headers: { 'X-Access-Token' : API_KEY }
});
const { data } = await balanceRes . json ();
if ( data . balance < requiredAmount ) {
throw new Error ( 'Insufficient balance' );
}
DUPLICATED_REF
Message : This reference ID is already in use
Cause : La référence a déjà été utilisée dans une requête précédente
Action :
Vérifiez le statut de la commande existante
Générez une nouvelle référence unique
Ne créez pas de commandes en double
const ref = `order- ${ Date . now () } - ${ Math . random (). toString ( 36 ). substr ( 2 , 9 ) } ` ;
IP_BLOCKED
Message : Your IP has been temporarily blocked
Cause : Trop de tentatives d’authentification échouées
Action :
Attendez 15 minutes pour le déblocage automatique
Vérifiez la clé API correcte
Contactez le support si le problème persiste
IP_NOT_ALLOWED
Message : Your IP address is not whitelisted
Cause : La liste blanche d’IP est activée
Action : Ajoutez votre IP à la liste blanche dans le tableau de bord
Erreurs de Validation (400)
ERR_VALIDATION
Message : Validation error
Cause : Les paramètres de requête ne respectent pas les exigences
Problèmes Courants : Champs manquants, types de données invalides, non-correspondance de motif
Action :
Validez les entrées côté client en premier
Vérifiez error.details pour les problèmes de champs spécifiques
Ne réessayez pas sans corriger le problème
// Validate before sending
function validateTopupRequest ( data ) {
const errors = [];
if ( ! data . plan_code ) {
errors . push ({ field: 'plan_code' , message: 'Plan code is required' });
}
if ( ! data . MSSIDN || ! / ^ 0 [ 567 ][ 0-9 ] {8} $ / . test ( data . MSSIDN )) {
errors . push ({ field: 'MSSIDN' , message: 'Invalid phone number format' });
}
if ( isDynamicPlan ( data . plan_code )) {
if ( ! data . amount || data . amount < 50 || data . amount > 5000 ) {
errors . push ({ field: 'amount' , message: 'Amount must be between 50 and 5000' });
}
}
return errors . length > 0 ? { valid: false , errors } : { valid: true };
}
ERR_PHONE
Message : Invalid phone number
Cause : Le numéro de téléphone est incorrect ou n’existe pas
Action : Utilisez /v3/internet/check-number pour valider en premier
const checkRes = await fetch (
`https://api.oneclickdz.com/v3/internet/check-number?type=ADSL&number= ${ number } ` ,
{ headers: { 'X-Access-Token' : API_KEY } }
);
if ( ! checkRes . ok ) {
throw new Error ( 'Invalid phone number' );
}
ERR_STOCK
Message : Product out of stock
Cause : Le produit/la valeur de carte demandé(e) n’est pas disponible
Action :
Vérifiez le stock avant de commander
Proposez des dénominations alternatives
Réessayez ultérieurement
NOT_FOUND - Message : Resource not found - Cause : La ressource demandée n’existe pas - Cas Courants : ID de commande invalide, référence
invalide, ressource supprimée - Action : - Vérifiez que l’ID/la référence
est correct(e) - Vérifiez les fautes de frappe - Gérez de manière élégante
dans l’interface utilisateur
Erreurs de Limite de Taux (429)
RATE_LIMIT_EXCEEDED
Message : Too many requests
Cause : La limite de taux a été dépassée
Limites : Sandbox : 60 req/min, Production : 120 req/min
Action : Implémentez un backoff exponentiel avec une logique de nouvelle tentative
async function fetchWithRetry ( url , options , maxRetries = 3 ) {
for ( let i = 0 ; i < maxRetries ; i ++ ) {
const response = await fetch ( url , options );
if ( response . status === 429 ) {
const retryAfter = response . headers . get ( 'Retry-After' ) || Math . pow ( 2 , i );
await new Promise ( resolve => setTimeout ( resolve , retryAfter * 1000 ));
continue ;
}
return response ;
}
throw new Error ( 'Max retries exceeded' );
}
Erreurs Serveur (500/503)
INTERNAL_SERVER_ERROR / INTERNAL_ERROR
Message : Developer was notified and will check shortly
Cause : Erreur inattendue sur nos serveurs
Action :
Ne remboursez pas immédiatement - attendez 24 heures
Sauvegardez le requestId pour le support
Implémentez une logique de nouvelle tentative avec backoff
Contactez le support avec les détails
Nous sommes automatiquement notifiés
if ( response . status === 500 ) {
console . error ( 'Server error:' , data . requestId );
// Mark for review - don't refund yet
await scheduleStatusCheck ( orderId , 24 * 60 * 60 * 1000 );
}
ERR_SERVICE
Message : Service temporarily unavailable
Cause : Maintenance du service ou problème temporaire
Action :
Affichez un message de maintenance
Réessayez après un délai
Surveillez la résolution
Bonnes Pratiques d’Implémentation
1. Vérifiez Toujours le Champ success
const response = await fetch ( url , options );
const data = await response . json ();
if ( data . success ) {
return data . data ;
} else {
throw new Error ( ` ${ data . error . code } : ${ data . error . message } ` );
}
2. Gérez les Codes d’Erreur Spécifiques
async function sendTopUp ( params ) {
const response = await fetch ( url , {
method: "POST" ,
headers: {
"Content-Type" : "application/json" ,
"X-Access-Token" : API_KEY ,
},
body: JSON . stringify ( params ),
});
const data = await response . json ();
if ( ! data . success ) {
switch ( data . error . code ) {
case "NO_BALANCE" :
case "INSUFFICIENT_BALANCE" :
throw new Error ( "Insufficient balance. Please add funds." );
case "DUPLICATED_REF" :
return await checkOrderStatus ( params . ref );
case "ERR_VALIDATION" :
throw new Error ( `Invalid input: ${ data . error . message } ` );
case "INTERNAL_SERVER_ERROR" :
case "INTERNAL_ERROR" :
await scheduleStatusCheck ( params . ref );
throw new Error ( "Temporary error, checking status later" );
default :
throw new Error ( ` ${ data . error . code } : ${ data . error . message } ` );
}
}
return data . data ;
}
3. Logique de Nouvelle Tentative Intelligente avec Backoff Exponentiel
async function safeApiCall ( apiFunction , options = {}) {
const { maxRetries = 3 , retryDelay = 1000 , retryOn = [ 500 , 503 ] } = options ;
let lastError ;
for ( let attempt = 1 ; attempt <= maxRetries ; attempt ++ ) {
try {
return await apiFunction ();
} catch ( error ) {
lastError = error ;
// Don't retry on client errors (4xx)
if ( error . status >= 400 && error . status < 500 ) {
throw error ;
}
// Retry on specific status codes
if ( retryOn . includes ( error . status ) && attempt < maxRetries ) {
const delay = retryDelay * Math . pow ( 2 , attempt - 1 );
console . log ( `Retry ${ attempt } / ${ maxRetries } after ${ delay } ms` );
await new Promise (( resolve ) => setTimeout ( resolve , delay ));
continue ;
}
throw error ;
}
}
throw lastError ;
}
// Usage
try {
const result = await safeApiCall (() => sendMobileTopup ( data ), {
maxRetries: 3 ,
retryDelay: 2000 ,
});
} catch ( error ) {
handleError ( error );
}
4. Messages d’Erreur Conviviaux
const errorMessages = {
MISSING_ACCESS_TOKEN: "Authentication required. Please check your API key." ,
INVALID_ACCESS_TOKEN: "Invalid API key. Please verify your credentials." ,
ERR_AUTH: "Authentication failed. Please check your API key." ,
NO_BALANCE: "Insufficient balance. Please top up your account." ,
INSUFFICIENT_BALANCE: "Insufficient balance. Please top up your account." ,
DUPLICATED_REF: "This order was already processed." ,
ERR_VALIDATION: "Please check your input and try again." ,
ERR_PHONE: "Invalid phone number. Please verify and try again." ,
ERR_STOCK: "This product is currently out of stock." ,
NOT_FOUND: "The requested resource was not found." ,
RATE_LIMIT_EXCEEDED: "Too many requests. Please wait a moment and try again." ,
ERR_SERVICE: "Service temporarily unavailable. Please try again." ,
INTERNAL_SERVER_ERROR: "An error occurred. Our team has been notified." ,
INTERNAL_ERROR: "An error occurred. Our team has been notified." ,
};
function getUserFriendlyMessage ( errorCode , defaultMessage ) {
return (
errorMessages [ errorCode ] ||
defaultMessage ||
"An unexpected error occurred."
);
}
// Usage
if ( ! data . success ) {
const userMessage = getUserFriendlyMessage (
data . error . code ,
data . error . message
);
showErrorToUser ( userMessage );
}
5. Enregistrez les Request IDs
function logError ( error , context ) {
const errorLog = {
timestamp: new Date (). toISOString (),
requestId: error . requestId ,
code: error . code ,
message: error . message ,
context: {
userId: context . userId ,
operation: context . operation ,
// Don't log sensitive data
},
};
// Log to your logging service
logger . error ( "API Error" , errorLog );
// Alert on critical errors
if ( error . status >= 500 ) {
alertOps ( "API Error" , errorLog );
}
}
Patterns Avancés
Pattern Circuit Breaker
Évitez les défaillances en cascade en interrompant les requêtes lorsque le taux d’erreur est élevé :
class CircuitBreaker {
constructor ( threshold = 5 , timeout = 60000 ) {
this . failureCount = 0 ;
this . threshold = threshold ;
this . timeout = timeout ;
this . state = "CLOSED" ; // CLOSED, OPEN, HALF_OPEN
this . nextAttempt = Date . now ();
}
async execute ( fn ) {
if ( this . state === "OPEN" ) {
if ( Date . now () < this . nextAttempt ) {
throw new Error ( "Circuit breaker is OPEN" );
}
this . state = "HALF_OPEN" ;
}
try {
const result = await fn ();
this . onSuccess ();
return result ;
} catch ( error ) {
this . onFailure ();
throw error ;
}
}
onSuccess () {
this . failureCount = 0 ;
this . state = "CLOSED" ;
}
onFailure () {
this . failureCount ++ ;
if ( this . failureCount >= this . threshold ) {
this . state = "OPEN" ;
this . nextAttempt = Date . now () + this . timeout ;
}
}
}
// Usage
const breaker = new CircuitBreaker ( 5 , 60000 );
try {
const result = await breaker . execute (() => sendTopup ( data ));
} catch ( error ) {
if ( error . message === "Circuit breaker is OPEN" ) {
showMaintenanceMessage ();
}
}
Surveillance des erreurs
Suivez les patterns d’erreurs pour identifier les problèmes tôt :
class ApiClient {
constructor ( apiKey ) {
this . apiKey = apiKey ;
this . errorStats = new Map ();
}
trackError ( errorCode ) {
const count = this . errorStats . get ( errorCode ) || 0 ;
this . errorStats . set ( errorCode , count + 1 );
}
async request ( url , options ) {
try {
const response = await fetch ( url , {
... options ,
headers: { ... options . headers , "X-Access-Token" : this . apiKey },
});
const data = await response . json ();
if ( ! data . success ) {
this . trackError ( data . error . code );
if ( this . errorStats . get ( data . error . code ) > 10 ) {
this . sendAlert ( `High error rate: ${ data . error . code } ` );
}
}
return data ;
} catch ( error ) {
this . trackError ( "NETWORK_ERROR" );
throw error ;
}
}
sendAlert ( message ) {
console . error ( "ALERT:" , message );
}
}
Gestion de UNKNOWN_ERROR
Important : Ne remboursez jamais immédiatement lors d’un UNKNOWN_ERROR. Attendez toujours 24 heures pour la résolution.
async function handleUnknownError ( orderId , topupId ) {
await db . orders . update ({
where: { id: orderId },
data: {
status: "REVIEW_NEEDED" ,
reviewReason: "UNKNOWN_ERROR from API" ,
reviewScheduled: new Date ( Date . now () + 24 * 60 * 60 * 1000 ),
},
});
await notifyUser ( orderId , {
title: "Order Under Review" ,
message: "Your order is being verified. Status will update within 24 hours." ,
});
}
Test des scénarios d’erreur
Utilisez le mode sandbox pour tester la gestion des erreurs :
// Test insufficient balance
// Set your sandbox balance very low
// Test validation errors
await sendTopUp ({
plan_code: 'INVALID_PLAN' ,
MSSIDN: '123456789' , // Invalid format
amount: - 100 // Negative amount
});
// Test duplicate reference
const ref = 'test-duplicate-123' ;
await sendTopUp ({ ref , ... }); // First request
await sendTopUp ({ ref , ... }); // Should fail with DUPLICATED_REF
Référence rapide
Valider tôt Valider les entrées côté client avant les appels API
Réessayer intelligemment Utiliser le backoff exponentiel pour les erreurs 5xx, jamais pour les 4xx
Journaliser le contexte Toujours inclure requestId et contexte dans les logs
Retour utilisateur Afficher des messages d’erreur clairs et actionnables aux utilisateurs
Étapes suivantes
Endpoints principaux Explorer tous les endpoints
Format de réponse Comprendre les réponses API