Passer au contenu principal

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.

Vue d’ensemble

Après l’envoi d’une demande de recharge, interrogez l’endpoint de statut jusqu’à ce que la transaction atteigne un état final. L’API traite généralement les demandes en 5 à 30 secondes, en passant par les états PENDING → HANDLING → FULFILLED/REFUNDED.
Critique : Gérez correctement toutes les valeurs de statut, en particulier UNKNOWN_ERROR. Ne remboursez jamais immédiatement en cas de statut inconnu !

API Vérification par référence

GET /v3/mobile/check-ref/:ref - Suivi avec votre référence

API Vérification par ID

GET /v3/mobile/check-id/:id - Suivi avec l’ID de recharge

Valeurs de statut

La compréhension de chaque statut est essentielle pour une gestion correcte :
Consultez la Référence API Vérification par ID pour des descriptions détaillées des statuts et leurs cas d’usage.
StatutSignificationDuréeAction
PENDINGEn file d’attente pour traitement2-15 secondesContinuer l’interrogation
HANDLINGEn cours de traitement3-8 secondesContinuer l’interrogation
FULFILLEDTerminé avec succès ✅FinalMarquer comme terminé, notifier l’utilisateur
REFUNDEDÉchoué et remboursé ❌FinalRembourser l’utilisateur, afficher l’erreur
UNKNOWN_ERRORStatut incertain ⚠️Se résout dans 1-24hAttendre, ne pas rembourser encore

Implémentation de base de l’interrogation

async function checkTopUpStatus(ref) {
  const response = await fetch(
    `https://api.oneclickdz.com/v3/mobile/check-ref/${ref}`,
    {
      headers: {
        'X-Access-Token': process.env.ONECLICKDZ_API_KEY
      }
    }
  );
  
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  
  const data = await response.json();
  
  if (!data.success) {
    throw new Error('Failed to check status');
  }
  
  return data.data;
}

async function pollTopUpStatus(ref, maxAttempts = 60, intervalMs = 5000) {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      const status = await checkTopUpStatus(ref);
      
      console.log(`[${attempt}/${maxAttempts}] Status: ${status.status}`);
      
      // Check if reached final state
      const finalStates = ['FULFILLED', 'REFUNDED', 'UNKNOWN_ERROR'];
      if (finalStates.includes(status.status)) {
        return status;
      }
      
      // Still processing, wait before next check
      if (attempt < maxAttempts) {
        await new Promise(resolve => setTimeout(resolve, intervalMs));
      }
    } catch (error) {
      console.error(`Attempt ${attempt} failed:`, error.message);
      
      // Continue polling even if one check fails
      if (attempt < maxAttempts) {
        await new Promise(resolve => setTimeout(resolve, intervalMs));
      }
    }
  }
  
  // Timeout
  return {
    status: 'TIMEOUT',
    message: 'Status check timeout. Check again later.'
  };
}

// Usage
const status = await pollTopUpStatus('order-123456');
console.log('Final status:', status.status);

Gérer les différents états

async function handleTopUpStatus(orderId, ref) {
  const status = await pollTopUpStatus(ref);
  
  switch (status.status) {
    case 'FULFILLED':
      // Success! Mark order as complete
      await db.orders.update({
        status: 'COMPLETED',
        completedAt: new Date()
      }, {
        where: { id: orderId }
      });
      
      // Notify user
      await notifyUser(orderId, 'success', 'Top-up completed successfully');
      
      console.log('✅ Top-up completed successfully');
      break;
      
    case 'REFUNDED':
      // Failed, refund user
      await db.orders.update({
        status: 'REFUNDED',
        refundMessage: status.refund_message,
        refundedAt: new Date()
      }, {
        where: { id: orderId }
      });
      
      // Refund user balance
      const order = await db.orders.findOne({ where: { id: orderId } });
      await refundUserBalance(order.userId, order.cost);
      
      // Notify user with error message (in Arabic)
      await notifyUser(orderId, 'failed', status.refund_message);
      
      // Show suggested offers if available
      if (status.suggested_offers && status.suggested_offers.length > 0) {
        console.log('Suggested alternatives:', status.suggested_offers);
        await showSuggestedOffers(orderId, status.suggested_offers);
      }
      
      console.log('❌ Top-up refunded:', status.refund_message);
      break;
      
    case 'UNKNOWN_ERROR':
      // Status uncertain - DO NOT REFUND YET!
      await db.orders.update({
        status: 'PENDING_VERIFICATION',
        verificationMessage: status.refund_message
      }, {
        where: { id: orderId }
      });
      
      // Show message to user (don't say it failed!)
      await notifyUser(orderId, 'pending', status.refund_message);
      
      // Schedule recheck in 1 hour
      await scheduleStatusRecheck(orderId, ref, Date.now() + 3600000);
      
      console.log('⚠️ Status unknown, will verify within 24h');
      break;
      
    case 'TIMEOUT':
      // Polling timeout - schedule recheck
      await db.orders.update({
        status: 'CHECKING'
      }, {
        where: { id: orderId }
      });
      
      // Schedule recheck in 5 minutes
      await scheduleStatusRecheck(orderId, ref, Date.now() + 300000);
      
      console.log('⏱️ Polling timeout, will check again later');
      break;
  }
}

Interrogation de statut en arrière-plan

Pour de meilleures performances, interrogez le statut dans des tâches en arrière-plan :
const Queue = require('bull');
const pollQueue = new Queue('status-polling');

// Start polling job
async function startStatusPolling(orderId, ref) {
  await pollQueue.add({
    orderId,
    ref,
    attempt: 1
  }, {
    delay: 5000, // Start after 5 seconds
    attempts: 60,
    backoff: {
      type: 'fixed',
      delay: 5000
    }
  });
}

// Process polling jobs
pollQueue.process(async (job) => {
  const { orderId, ref, attempt } = job.data;
  
  console.log(`Checking status (attempt ${attempt})...`);
  
  const status = await checkTopUpStatus(ref);
  
  const finalStates = ['FULFILLED', 'REFUNDED', 'UNKNOWN_ERROR'];
  
  if (finalStates.includes(status.status)) {
    // Reached final state, handle it
    await handleTopUpStatus(orderId, ref);
    return { done: true, status: status.status };
  }
  
  // Still processing, continue polling
  if (attempt < 60) {
    await pollQueue.add({
      orderId,
      ref,
      attempt: attempt + 1
    }, {
      delay: 5000
    });
  } else {
    // Timeout
    await handleTopUpStatus(orderId, ref);
  }
  
  return { done: false, status: status.status };
});

Revérification planifiée pour UNKNOWN_ERROR

Configurez une tâche cron quotidienne pour revérifier les commandes incertaines :
const cron = require('node-cron');

// Run daily at midnight
cron.schedule('0 0 * * *', async () => {
  console.log('🔄 Checking uncertain orders...');
  
  // Find all orders with UNKNOWN_ERROR or PENDING_VERIFICATION
  // that are older than 1 hour
  const uncertainOrders = await db.orders.findAll({
    where: {
      status: {
        [db.Op.in]: ['UNKNOWN_ERROR', 'PENDING_VERIFICATION']
      },
      createdAt: {
        [db.Op.lt]: new Date(Date.now() - 3600000) // 1 hour ago
      }
    }
  });
  
  console.log(`Found ${uncertainOrders.length} orders to recheck`);
  
  for (const order of uncertainOrders) {
    try {
      const status = await checkTopUpStatus(order.ref);
      
      console.log(`Order ${order.id}: ${order.status}${status.status}`);
      
      // Update based on new status
      if (status.status === 'FULFILLED') {
        await db.orders.update({
          status: 'COMPLETED',
          completedAt: new Date()
        }, {
          where: { id: order.id }
        });
        
        await notifyUser(order.id, 'success', 'Top-up completed successfully');
        
      } else if (status.status === 'REFUNDED') {
        await db.orders.update({
          status: 'REFUNDED',
          refundMessage: status.refund_message,
          refundedAt: new Date()
        }, {
          where: { id: order.id }
        });
        
        // NOW we can refund
        await refundUserBalance(order.userId, order.cost);
        await notifyUser(order.id, 'failed', status.refund_message);
      }
      
    } catch (error) {
      console.error(`Failed to recheck order ${order.id}:`, error);
    }
  }
  
  console.log('✅ Recheck completed');
});

Stratégie d’interrogation optimisée

Utilisez des intervalles d’interrogation intelligents :
async function adaptivePollTopUpStatus(ref) {
  const intervals = [
    { attempts: 3, delay: 3000 },   // First 3 checks: every 3s
    { attempts: 5, delay: 5000 },   // Next 5 checks: every 5s
    { attempts: 12, delay: 10000 }, // Next 12 checks: every 10s
    { attempts: 40, delay: 30000 }  // Final checks: every 30s
  ];
  
  let totalAttempts = 0;
  
  for (const { attempts, delay } of intervals) {
    for (let i = 0; i < attempts; i++) {
      totalAttempts++;
      
      const status = await checkTopUpStatus(ref);
      
      console.log(`[${totalAttempts}] Status: ${status.status} (delay: ${delay}ms)`);
      
      const finalStates = ['FULFILLED', 'REFUNDED', 'UNKNOWN_ERROR'];
      if (finalStates.includes(status.status)) {
        return status;
      }
      
      // Wait before next check
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
  
  return { status: 'TIMEOUT' };
}

Bonnes pratiques

Attendez que le statut se résolve en FULFILLED ou REFUNDED dans les 24 heures avant de traiter les remboursements.
Interrogez le statut de manière asynchrone pour éviter de bloquer les requêtes utilisateur et améliorer les performances.
Définissez un nombre maximum de tentatives d’interrogation (généralement 60 = 5 minutes) et replanifiez les vérifications si nécessaire.
Enregistrez le statut dans votre base de données pour minimiser les appels API. N’interrogez l’API que lorsque le statut n’est pas final.
Affichez toujours le refund_message tel quel aux utilisateurs. Il est en arabe et explique clairement le problème.
Lorsque suggested_offers est présent, affichez ces alternatives pour améliorer la conversion.

Étapes suivantes

Stratégies d'interrogation

Apprenez les techniques avancées d’optimisation de l’interrogation

Webhooks

Configurez des notifications de statut en temps réel au lieu d’interroger

API Liste des recharges

Consultez toutes vos transactions de recharge

Gestion des erreurs

Référence complète de gestion des erreurs