> ## 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.

# Step 4: Status Polling

> Track top-up status until completion

## Overview

After sending a top-up request, poll the status endpoint until the transaction reaches a final state. The API typically processes requests in 5-30 seconds, going through PENDING → HANDLING → FULFILLED/REFUNDED states.

<Warning>
  **Critical:** Properly handle all status values, especially `UNKNOWN_ERROR`. Never refund immediately on unknown status!
</Warning>

<CardGroup cols={2}>
  <Card title="Check by Reference API" icon="tag" href="/en/api-reference/mobile/check-by-ref">
    GET /v3/mobile/check-ref/:ref - Track using your reference
  </Card>

  <Card title="Check by ID API" icon="id-card" href="/en/api-reference/mobile/check-by-id">
    GET /v3/mobile/check-id/:id - Track using topup ID
  </Card>
</CardGroup>

## Status Values

Understanding each status is critical for proper handling:

<Info>
  See [Check by ID API Reference](/en/api-reference/mobile/check-by-id#status-values) for detailed status descriptions and use cases.
</Info>

| Status             | Meaning                  | Duration          | Action                     |
| ------------------ | ------------------------ | ----------------- | -------------------------- |
| **PENDING**        | Queued for processing    | 2-15 seconds      | Continue polling           |
| **HANDLING**       | Being processed          | 3-8 seconds       | Continue polling           |
| **FULFILLED**      | Successfully completed ✅ | Final             | Mark complete, notify user |
| **REFUNDED**       | Failed and refunded ❌    | Final             | Refund user, show error    |
| **UNKNOWN\_ERROR** | Status uncertain ⚠️      | Resolves in 1-24h | Wait, don't refund yet     |

## Basic Polling Implementation

<CodeGroup>
  ```javascript Node.js theme={null}
  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);
  ```

  ```python Python theme={null}
  import requests
  import time
  import os

  def check_topup_status(ref):
      """Check top-up status by reference"""
      response = requests.get(
          f'https://api.oneclickdz.com/v3/mobile/check-ref/{ref}',
          headers={'X-Access-Token': os.getenv('ONECLICKDZ_API_KEY')}
      )
      
      response.raise_for_status()
      data = response.json()
      
      if not data['success']:
          raise Exception('Failed to check status')
      
      return data['data']

  def poll_topup_status(ref, max_attempts=60, interval_seconds=5):
      """Poll top-up status until completion"""
      for attempt in range(1, max_attempts + 1):
          try:
              status = check_topup_status(ref)
              
              print(f'[{attempt}/{max_attempts}] Status: {status["status"]}')
              
              # Check if reached final state
              final_states = ['FULFILLED', 'REFUNDED', 'UNKNOWN_ERROR']
              if status['status'] in final_states:
                  return status
              
              # Still processing, wait before next check
              if attempt < max_attempts:
                  time.sleep(interval_seconds)
                  
          except Exception as error:
              print(f'Attempt {attempt} failed: {error}')
              
              # Continue polling even if one check fails
              if attempt < max_attempts:
                  time.sleep(interval_seconds)
      
      # Timeout
      return {
          'status': 'TIMEOUT',
          'message': 'Status check timeout. Check again later.'
      }

  # Usage
  status = poll_topup_status('order-123456')
  print(f'Final status: {status["status"]}')
  ```

  ```php PHP theme={null}
  <?php

  function checkTopUpStatus($ref) {
      $ch = curl_init("https://api.oneclickdz.com/v3/mobile/check-ref/{$ref}");
      
      curl_setopt_array($ch, [
          CURLOPT_RETURNTRANSFER => true,
          CURLOPT_HTTPHEADER => [
              'X-Access-Token: ' . getenv('ONECLICKDZ_API_KEY')
          ]
      ]);
      
      $response = curl_exec($ch);
      $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
      curl_close($ch);
      
      if ($httpCode !== 200) {
          throw new Exception("HTTP error! status: $httpCode");
      }
      
      $data = json_decode($response, true);
      
      if (!$data['success']) {
          throw new Exception('Failed to check status');
      }
      
      return $data['data'];
  }

  function pollTopUpStatus($ref, $maxAttempts = 60, $intervalSeconds = 5) {
      for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
          try {
              $status = checkTopUpStatus($ref);
              
              echo "[{$attempt}/{$maxAttempts}] Status: {$status['status']}\n";
              
              // Check if reached final state
              $finalStates = ['FULFILLED', 'REFUNDED', 'UNKNOWN_ERROR'];
              if (in_array($status['status'], $finalStates)) {
                  return $status;
              }
              
              // Still processing, wait before next check
              if ($attempt < $maxAttempts) {
                  sleep($intervalSeconds);
              }
              
          } catch (Exception $error) {
              echo "Attempt {$attempt} failed: {$error->getMessage()}\n";
              
              // Continue polling even if one check fails
              if ($attempt < $maxAttempts) {
                  sleep($intervalSeconds);
              }
          }
      }
      
      // Timeout
      return [
          'status' => 'TIMEOUT',
          'message' => 'Status check timeout. Check again later.'
      ];
  }

  // Usage
  $status = pollTopUpStatus('order-123456');
  echo "Final status: {$status['status']}\n";
  ```

  ```bash cURL Script theme={null}
  #!/bin/bash

  API_KEY="YOUR_API_KEY"
  REF="$1"
  MAX_ATTEMPTS=60
  INTERVAL=5

  check_status() {
      curl -s "https://api.oneclickdz.com/v3/mobile/check-ref/$1" \
          -H "X-Access-Token: $API_KEY" | jq -r '.data.status'
  }

  poll_status() {
      for attempt in $(seq 1 $MAX_ATTEMPTS); do
          status=$(check_status "$1")
          echo "[$attempt/$MAX_ATTEMPTS] Status: $status"
          
          # Check if final state
          if [[ "$status" == "FULFILLED" ]] || \
             [[ "$status" == "REFUNDED" ]] || \
             [[ "$status" == "UNKNOWN_ERROR" ]]; then
              echo "Final status: $status"
              return 0
          fi
          
          # Wait before next check
          if [ $attempt -lt $MAX_ATTEMPTS ]; then
              sleep $INTERVAL
          fi
      done
      
      echo "Timeout after $MAX_ATTEMPTS attempts"
      return 1
  }

  poll_status "$REF"
  ```
</CodeGroup>

## Handling Different Status States

<CodeGroup>
  ```javascript Node.js theme={null}
  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;
    }
  }
  ```

  ```python Python theme={null}
  async def handle_topup_status(order_id, ref, db):
      """Handle different top-up status outcomes"""
      status = poll_topup_status(ref)
      
      if status['status'] == 'FULFILLED':
          # Success! Mark order as complete
          db.orders.update_one(
              {'_id': order_id},
              {
                  '$set': {
                      'status': 'COMPLETED',
                      'completed_at': datetime.now()
                  }
              }
          )
          
          # Notify user
          notify_user(order_id, 'success', 'Top-up completed successfully')
          
          print('✅ Top-up completed successfully')
          
      elif status['status'] == 'REFUNDED':
          # Failed, refund user
          db.orders.update_one(
              {'_id': order_id},
              {
                  '$set': {
                      'status': 'REFUNDED',
                      'refund_message': status.get('refund_message'),
                      'refunded_at': datetime.now()
                  }
              }
          )
          
          # Refund user balance
          order = db.orders.find_one({'_id': order_id})
          refund_user_balance(order['user_id'], order['cost'])
          
          # Notify user
          notify_user(order_id, 'failed', status.get('refund_message'))
          
          # Show suggested offers
          if status.get('suggested_offers'):
              print('Suggested alternatives:', status['suggested_offers'])
              show_suggested_offers(order_id, status['suggested_offers'])
          
          print(f"❌ Top-up refunded: {status.get('refund_message')}")
          
      elif status['status'] == 'UNKNOWN_ERROR':
          # Status uncertain - DO NOT REFUND YET!
          db.orders.update_one(
              {'_id': order_id},
              {
                  '$set': {
                      'status': 'PENDING_VERIFICATION',
                      'verification_message': status.get('refund_message')
                  }
              }
          )
          
          # Notify user (don't say it failed!)
          notify_user(order_id, 'pending', status.get('refund_message'))
          
          # Schedule recheck in 1 hour
          schedule_status_recheck(order_id, ref, time.time() + 3600)
          
          print('⚠️ Status unknown, will verify within 24h')
          
      elif status['status'] == 'TIMEOUT':
          # Polling timeout
          db.orders.update_one(
              {'_id': order_id},
              {'$set': {'status': 'CHECKING'}}
          )
          
          # Schedule recheck in 5 minutes
          schedule_status_recheck(order_id, ref, time.time() + 300)
          
          print('⏱️ Polling timeout, will check again later')
  ```

  ```php PHP theme={null}
  <?php

  function handleTopUpStatus($orderId, $ref, $db) {
      $status = pollTopUpStatus($ref);
      
      switch ($status['status']) {
          case 'FULFILLED':
              // Success! Mark order as complete
              $db->query(
                  "UPDATE orders SET status = 'COMPLETED', completed_at = NOW() WHERE id = ?",
                  [$orderId]
              );
              
              // Notify user
              notifyUser($orderId, 'success', 'Top-up completed successfully');
              
              echo "✅ Top-up completed successfully\n";
              break;
              
          case 'REFUNDED':
              // Failed, refund user
              $db->query(
                  "UPDATE orders 
                   SET status = 'REFUNDED', refund_message = ?, refunded_at = NOW() 
                   WHERE id = ?",
                  [$status['refund_message'] ?? null, $orderId]
              );
              
              // Refund user balance
              $order = $db->query("SELECT * FROM orders WHERE id = ?", [$orderId])
                          ->fetch(PDO::FETCH_ASSOC);
              refundUserBalance($order['user_id'], $order['cost'], $db);
              
              // Notify user
              notifyUser($orderId, 'failed', $status['refund_message'] ?? 'Top-up failed');
              
              // Show suggested offers
              if (!empty($status['suggested_offers'])) {
                  echo "Suggested alternatives: " . json_encode($status['suggested_offers']) . "\n";
                  showSuggestedOffers($orderId, $status['suggested_offers']);
              }
              
              echo "❌ Top-up refunded: {$status['refund_message']}\n";
              break;
              
          case 'UNKNOWN_ERROR':
              // Status uncertain - DO NOT REFUND YET!
              $db->query(
                  "UPDATE orders 
                   SET status = 'PENDING_VERIFICATION', verification_message = ? 
                   WHERE id = ?",
                  [$status['refund_message'] ?? null, $orderId]
              );
              
              // Notify user (don't say it failed!)
              notifyUser($orderId, 'pending', $status['refund_message'] ?? 'Verifying status...');
              
              // Schedule recheck in 1 hour
              scheduleStatusRecheck($orderId, $ref, time() + 3600);
              
              echo "⚠️ Status unknown, will verify within 24h\n";
              break;
              
          case 'TIMEOUT':
              // Polling timeout
              $db->query("UPDATE orders SET status = 'CHECKING' WHERE id = ?", [$orderId]);
              
              // Schedule recheck in 5 minutes
              scheduleStatusRecheck($orderId, $ref, time() + 300);
              
              echo "⏱️ Polling timeout, will check again later\n";
              break;
      }
  }
  ```
</CodeGroup>

## Background Status Polling

For better performance, poll status in background jobs:

<CodeGroup>
  ```javascript Node.js (Bull Queue) theme={null}
  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 };
  });
  ```

  ```python Python (Celery) theme={null}
  from celery import Celery

  app = Celery('polling', broker='redis://localhost:6379/0')

  @app.task(bind=True, max_retries=60)
  def poll_status_task(self, order_id, ref):
      """Poll status asynchronously"""
      try:
          status = check_topup_status(ref)
          
          print(f'Checking status (attempt {self.request.retries + 1})...')
          
          final_states = ['FULFILLED', 'REFUNDED', 'UNKNOWN_ERROR']
          
          if status['status'] in final_states:
              # Reached final state
              handle_topup_status(order_id, ref, db)
              return {'done': True, 'status': status['status']}
          
          # Still processing, retry in 5 seconds
          raise self.retry(countdown=5)
          
      except Exception as error:
          if self.request.retries >= self.max_retries:
              # Timeout
              handle_topup_status(order_id, ref, db)
              return {'done': True, 'status': 'TIMEOUT'}
          
          # Retry
          raise self.retry(countdown=5, exc=error)

  # Start polling
  def start_status_polling(order_id, ref):
      poll_status_task.apply_async(args=[order_id, ref], countdown=5)
  ```
</CodeGroup>

## Scheduled Recheck for UNKNOWN\_ERROR

Set up daily cronjob to recheck uncertain orders:

<CodeGroup>
  ```javascript Node.js (Cron) theme={null}
  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');
  });
  ```

  ```python Python (Schedule) theme={null}
  import schedule
  import time
  from datetime import datetime, timedelta

  def recheck_uncertain_orders():
      """Daily cronjob to recheck uncertain orders"""
      print('🔄 Checking uncertain orders...')
      
      # Find uncertain orders older than 1 hour
      one_hour_ago = datetime.now() - timedelta(hours=1)
      
      uncertain_orders = db.orders.find({
          'status': {'$in': ['UNKNOWN_ERROR', 'PENDING_VERIFICATION']},
          'created_at': {'$lt': one_hour_ago}
      })
      
      uncertain_orders = list(uncertain_orders)
      print(f'Found {len(uncertain_orders)} orders to recheck')
      
      for order in uncertain_orders:
          try:
              status = check_topup_status(order['ref'])
              
              print(f"Order {order['_id']}: {order['status']} → {status['status']}")
              
              if status['status'] == 'FULFILLED':
                  db.orders.update_one(
                      {'_id': order['_id']},
                      {
                          '$set': {
                              'status': 'COMPLETED',
                              'completed_at': datetime.now()
                          }
                      }
                  )
                  
                  notify_user(order['_id'], 'success', 'Top-up completed successfully')
                  
              elif status['status'] == 'REFUNDED':
                  db.orders.update_one(
                      {'_id': order['_id']},
                      {
                          '$set': {
                              'status': 'REFUNDED',
                              'refund_message': status.get('refund_message'),
                              'refunded_at': datetime.now()
                          }
                      }
                  )
                  
                  # NOW we can refund
                  refund_user_balance(order['user_id'], order['cost'])
                  notify_user(order['_id'], 'failed', status.get('refund_message'))
                  
          except Exception as error:
              print(f"Failed to recheck order {order['_id']}: {error}")
      
      print('✅ Recheck completed')

  # Schedule daily at midnight
  schedule.every().day.at("00:00").do(recheck_uncertain_orders)

  # Keep running
  while True:
      schedule.run_pending()
      time.sleep(60)
  ```

  ```bash Cron Script theme={null}
  #!/bin/bash
  # Save as: /usr/local/bin/recheck-topups.sh
  # Add to crontab: 0 0 * * * /usr/local/bin/recheck-topups.sh

  echo "🔄 Checking uncertain orders..."

  # Query database for uncertain orders (adjust based on your DB)
  # This is a pseudo-example
  uncertain_orders=$(mysql -u user -p'pass' -D db -se \
      "SELECT id, ref FROM orders 
       WHERE status IN ('UNKNOWN_ERROR', 'PENDING_VERIFICATION') 
       AND created_at < DATE_SUB(NOW(), INTERVAL 1 HOUR)")

  while IFS=$'\t' read -r order_id ref; do
      echo "Checking order $order_id ($ref)..."
      
      status=$(curl -s "https://api.oneclickdz.com/v3/mobile/check-ref/$ref" \
          -H "X-Access-Token: $API_KEY" | jq -r '.data.status')
      
      echo "Status: $status"
      
      if [ "$status" = "FULFILLED" ]; then
          mysql -u user -p'pass' -D db -e \
              "UPDATE orders SET status='COMPLETED', completed_at=NOW() WHERE id=$order_id"
          echo "✅ Order $order_id completed"
          
      elif [ "$status" = "REFUNDED" ]; then
          mysql -u user -p'pass' -D db -e \
              "UPDATE orders SET status='REFUNDED', refunded_at=NOW() WHERE id=$order_id"
          # Trigger refund process
          echo "❌ Order $order_id refunded"
      fi
  done <<< "$uncertain_orders"

  echo "✅ Recheck completed"
  ```
</CodeGroup>

## Optimized Polling Strategy

Use intelligent polling intervals:

<CodeGroup>
  ```javascript Node.js (Adaptive Intervals) theme={null}
  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' };
  }
  ```

  ```python Python (Adaptive Intervals) theme={null}
  def adaptive_poll_topup_status(ref):
      """Poll with adaptive intervals"""
      intervals = [
          {'attempts': 3, 'delay': 3},    # First 3 checks: every 3s
          {'attempts': 5, 'delay': 5},    # Next 5 checks: every 5s
          {'attempts': 12, 'delay': 10},  # Next 12 checks: every 10s
          {'attempts': 40, 'delay': 30}   # Final checks: every 30s
      ]
      
      total_attempts = 0
      
      for interval in intervals:
          for i in range(interval['attempts']):
              total_attempts += 1
              
              status = check_topup_status(ref)
              
              print(f"[{total_attempts}] Status: {status['status']} (delay: {interval['delay']}s)")
              
              final_states = ['FULFILLED', 'REFUNDED', 'UNKNOWN_ERROR']
              if status['status'] in final_states:
                  return status
              
              # Wait before next check
              time.sleep(interval['delay'])
      
      return {'status': 'TIMEOUT'}
  ```
</CodeGroup>

## Best Practices

<AccordionGroup>
  <Accordion title="Never Refund UNKNOWN_ERROR Immediately" icon="hand">
    Wait for status to resolve to FULFILLED or REFUNDED within 24 hours before processing refunds.
  </Accordion>

  <Accordion title="Use Background Jobs" icon="gear">
    Poll status asynchronously to avoid blocking user requests and improve performance.
  </Accordion>

  <Accordion title="Implement Timeout Protection" icon="clock">
    Set maximum polling attempts (typically 60 = 5 minutes) and reschedule checks if needed.
  </Accordion>

  <Accordion title="Cache Status in Database" icon="database">
    Store status in your database to minimize API calls. Only query API when status is not final.
  </Accordion>

  <Accordion title="Display Arabic Messages" icon="language">
    Always show the `refund_message` to users as-is. It's in Arabic and explains the issue clearly.
  </Accordion>

  <Accordion title="Handle Suggested Offers" icon="lightbulb">
    When `suggested_offers` is present, show these alternatives to improve conversion.
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Polling Strategies" icon="chart-line" href="/en/polling-strategies">
    Learn advanced polling optimization techniques
  </Card>

  <Card title="Webhooks" icon="webhook" href="/en/webhooks">
    Set up real-time status notifications instead of polling
  </Card>

  <Card title="List Top-Ups API" icon="list" href="/en/api-reference/mobile/list-topups">
    View all your top-up transactions
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation" href="/en/api-reference/error-handling">
    Complete error handling reference
  </Card>
</CardGroup>
