> ## 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 3: Status Polling

> Simple automated payment status checking

## Overview

You need to check payment status because bank processing is asynchronous. Use two simple methods:

1. **Cron job**: Check pending orders every 20 minutes
2. **Manual check**: When customer views their order

## Method 1: Cron Job (Every 20 Minutes)

<CodeGroup>
  ```javascript Node.js with node-cron theme={null}
  const cron = require("node-cron");

  // Run every 20 minutes
  cron.schedule("*/20 * * * *", async () => {
    console.log("Checking pending payments...");

    try {
      // Get pending orders from last 24 hours
      const pendingOrders = await db.orders.find({
        status: "PENDING",
        paymentRef: { $exists: true },
        createdAt: { $gte: new Date(Date.now() - 24 * 60 * 60 * 1000) },
      });

      for (const order of pendingOrders) {
        await checkAndUpdateOrder(order.id, order.paymentRef);
        await sleep(100); // Small delay between requests
      }

      console.log(`Checked ${pendingOrders.length} orders`);
    } catch (error) {
      console.error("Cron job error:", error);
    }
  });

  async function checkAndUpdateOrder(orderId, paymentRef) {
    try {
      const response = await fetch(
        `https://api.oneclickdz.com/v3/ocpay/checkPayment/${paymentRef}`,
        {
          headers: { "X-Access-Token": process.env.ONECLICK_API_KEY },
        }
      );

      const data = await response.json();
      const status = data.data.status;

      if (status === "CONFIRMED") {
        await db.orders.update(orderId, { status: "PAID" });
        console.log(`✅ Order ${orderId} marked as PAID`);
      } else if (status === "FAILED") {
        await db.orders.update(orderId, { status: "FAILED" });
        console.log(`❌ Order ${orderId} marked as FAILED`);
      }
    } catch (error) {
      console.error(`Error checking order ${orderId}:`, error);
    }
  }

  function sleep(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }
  ```

  ```python Python with APScheduler theme={null}
  from apscheduler.schedulers.background import BackgroundScheduler
  import requests
  import os
  from datetime import datetime, timedelta
  import time

  scheduler = BackgroundScheduler()

  # Run every 20 minutes
  @scheduler.scheduled_job('interval', minutes=20)
  def check_pending_payments():
      print('Checking pending payments...')

      try:
          # Get pending orders from last 24 hours
          yesterday = datetime.now() - timedelta(days=1)
          pending_orders = db.orders.find({
              'status': 'PENDING',
              'paymentRef': {'$exists': True},
              'createdAt': {'$gte': yesterday}
          })

          count = 0
          for order in pending_orders:
              check_and_update_order(order['id'], order['paymentRef'])
              time.sleep(0.1)  # Small delay between requests
              count += 1

          print(f'Checked {count} orders')
      except Exception as e:
          print(f'Cron job error: {e}')

  def check_and_update_order(order_id, payment_ref):
      try:
          response = requests.get(
              f'https://api.oneclickdz.com/v3/ocpay/checkPayment/{payment_ref}',
              headers={'X-Access-Token': os.getenv('ONECLICK_API_KEY')}
          )

          data = response.json()
          status = data['data']['status']

          if status == 'CONFIRMED':
              db.orders.update(order_id, {'status': 'PAID'})
              print(f'✅ Order {order_id} marked as PAID')
          elif status == 'FAILED':
              db.orders.update(order_id, {'status': 'FAILED'})
              print(f'❌ Order {order_id} marked as FAILED')
      except Exception as e:
          print(f'Error checking order {order_id}: {e}')

  # Start scheduler
  scheduler.start()
  ```

  ```php PHP with Cron theme={null}
  <?php
  // cron-check-payments.php
  // Add to crontab: */20 * * * * php /path/to/cron-check-payments.php

  require_once 'config.php';

  echo "Checking pending payments...\n";

  try {
      // Get pending orders from last 24 hours
      $yesterday = date('Y-m-d H:i:s', strtotime('-24 hours'));

      $stmt = $db->prepare("
          SELECT id, payment_ref
          FROM orders
          WHERE status = 'PENDING'
          AND payment_ref IS NOT NULL
          AND created_at >= ?
      ");
      $stmt->execute([$yesterday]);
      $pendingOrders = $stmt->fetchAll(PDO::FETCH_ASSOC);

      foreach ($pendingOrders as $order) {
          checkAndUpdateOrder($order['id'], $order['payment_ref']);
          usleep(100000); // 100ms delay
      }

      echo "Checked " . count($pendingOrders) . " orders\n";
  } catch (Exception $e) {
      echo "Cron job error: " . $e->getMessage() . "\n";
  }

  function checkAndUpdateOrder($orderId, $paymentRef) {
      global $db;

      try {
          $ch = curl_init("https://api.oneclickdz.com/v3/ocpay/checkPayment/{$paymentRef}");
          curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
          curl_setopt($ch, CURLOPT_HTTPHEADER, [
              'X-Access-Token: ' . getenv('ONECLICK_API_KEY')
          ]);

          $response = curl_exec($ch);
          $data = json_decode($response, true);
          $status = $data['data']['status'];

          if ($status === 'CONFIRMED') {
              $stmt = $db->prepare("UPDATE orders SET status = 'PAID' WHERE id = ?");
              $stmt->execute([$orderId]);
              echo "✅ Order {$orderId} marked as PAID\n";
          } elseif ($status === 'FAILED') {
              $stmt = $db->prepare("UPDATE orders SET status = 'FAILED' WHERE id = ?");
              $stmt->execute([$orderId]);
              echo "❌ Order {$orderId} marked as FAILED\n";
          }
      } catch (Exception $e) {
          echo "Error checking order {$orderId}: " . $e->getMessage() . "\n";
      }
  }
  ?>
  ```
</CodeGroup>

### Setup Cron Job

Add to your crontab (Linux/Unix):

```bash theme={null}
# Edit crontab
crontab -e

# Add line to run every 20 minutes
*/20 * * * * /usr/bin/node /path/to/cron-job.js >> /var/log/payment-check.log 2>&1

# Or for Python
*/20 * * * * /usr/bin/python3 /path/to/cron-job.py >> /var/log/payment-check.log 2>&1

# Or for PHP
*/20 * * * * /usr/bin/php /path/to/cron-check-payments.php >> /var/log/payment-check.log 2>&1
```

## Method 2: Manual Check (When Customer Views Order)

Already implemented in Step 2! When customer lands on order page, it automatically checks once.

Optional: Add a button to check on demand (checks every 1 minute max):

```html theme={null}
<!DOCTYPE html>
<html>
  <head>
    <title>Order Status</title>
  </head>
  <body>
    <h1>Order Status</h1>
    <p>Status: <span id="status">PENDING</span></p>

    <button id="checkBtn" onclick="startChecking()">
      Check Payment Status
    </button>

    <p id="message"></p>

    <script>
      let checking = false;
      let checkInterval = null;
      const orderId = window.location.pathname.split("/").pop();

      async function checkPayment() {
        const response = await fetch(`/orders/${orderId}/check`, {
          method: "POST",
        });

        const data = await response.json();
        document.getElementById("status").textContent = data.status;

        if (data.status === "CONFIRMED") {
          document.getElementById("message").textContent =
            "✅ Payment confirmed!";
          stopChecking();
          setTimeout(() => location.reload(), 2000);
        } else if (data.status === "FAILED") {
          document.getElementById("message").textContent = "❌ Payment failed";
          stopChecking();
        } else {
          document.getElementById("message").textContent =
            "⏳ Still pending...";
        }
      }

      function startChecking() {
        if (checking) return;

        checking = true;
        document.getElementById("checkBtn").disabled = true;

        // Check immediately
        checkPayment();

        // Then check every 1 minute
        checkInterval = setInterval(checkPayment, 60000);

        // Stop after 10 minutes
        setTimeout(stopChecking, 10 * 60000);
      }

      function stopChecking() {
        checking = false;
        document.getElementById("checkBtn").disabled = false;
        if (checkInterval) {
          clearInterval(checkInterval);
          checkInterval = null;
        }
      }

      // Auto-check when page loads (if pending)
      if (document.getElementById("status").textContent === "PENDING") {
        checkPayment();
      }
    </script>
  </body>
</html>
```

## Summary

<Check>
  **Two simple methods:**

  1. ✅ Cron job every 20 minutes for all pending orders
  2. ✅ Manual check when customer views order (max every 1 minute)
  3. ✅ Auto-check once when order page loads

  This covers all scenarios without overloading the API!
</Check>

## Next Steps

<Card title="Continue to Best Practices" icon="arrow-right" href="/en/ocpay-guides/4-best-practices">
  Learn essential security and error handling
</Card>
