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

# الخطوة 3: تتبع الحالة

> التحقق التلقائي والبسيط من حالة المدفوعات

<div dir="rtl">
  ## نظرة عامة

  تحتاج إلى التحقق من حالة المدفوعات لأن معالجة البنك تتم بشكل غير متزامن. استخدم طريقتين بسيطتين:

  1. **مهمة cron**: تحقق من الطلبات المعلقة كل 20 دقيقة
  2. **تحقق يدوي**: عندما يعرض العميل طلبه

  ## الطريقة الأولى: مهمة cron (كل 20 دقيقة)

  <CodeGroup>
    ```javascript Node.js مع 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 مع 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 مع 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>

  ### إعداد مهمة Cron

  أضف إلى 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
  ```

  ## الطريقة الثانية: التحقق اليدوي (عند عرض العميل لطلبه)

  تم تنفيذ ذلك بالفعل في الخطوة 2! عندما يصل العميل إلى صفحة الطلب، يتم التحقق تلقائياً مرة واحدة.

  اختياري: أضف زراً للتحقق عند الطلب (يتحقق بحد أقصى مرة في الدقيقة):

  ```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>
  ```

  ## ملخص

  <Check>
    **طريقتان بسيطتان:**

    1. ✅ مهمة cron كل 20 دقيقة لجميع الطلبات المعلقة
    2. ✅ تحقق يدوي عند عرض العميل لطلبه (بحد أقصى مرة في الدقيقة)
    3. ✅ تحقق تلقائي مرة واحدة عند تحميل صفحة الطلب

    يغطي هذا جميع السيناريوهات دون الإفراط في استدعاء API!
  </Check>

  ## الخطوات التالية

  <Card title="المتابعة إلى أفضل الممارسات" icon="arrow-right" href="/ar/ocpay-guides/4-best-practices">
    تعلّم ممارسات الأمان الأساسية ومعالجة الأخطاء
  </Card>
</div>
