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

# الخطوة 2: سير الدفع

> دليل بسيط لتنفيذ المدفوعات في تطبيقك

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

  سير الدفع بسيط:

  1. العميل ينشئ طلباً → احفظه بحالة `PENDING`
  2. أنشئ رابط دفع → احصل على `paymentRef`
  3. **احفظ `paymentRef` مع الطلب** (مهم!)
  4. وجّه العميل إلى صفحة الدفع
  5. العميل يدفع ويعود
  6. تحقق من حالة الدفع وحدّث الطلب

  ## التنفيذ الكامل

  ### الخطوة 1: إنشاء الطلب

  عند إنشاء العميل لطلب، احفظه بحالة `PENDING`:

  ```javascript theme={null}
  async function createOrder(customerData, items, total) {
    // Generate unique order ID
    const orderId = generateUniqueId(); // e.g., "ORD-2025-001234"

    // Save order to database
    const order = await db.orders.create({
      id: orderId,
      customerId: customerData.id,
      items: items,
      totalAmount: total,
      status: "PENDING", // Important: Start as PENDING
      paymentRef: null, // Will be set after creating payment link
      createdAt: new Date(),
      updatedAt: new Date(),
    });

    return order;
  }
  ```

  <Warning>
    **حرج**: يجب أن يكون لكل طلب معرّف فريد ويجب أن يبدأ بحالة `PENDING`
  </Warning>

  ### الخطوة 2: إنشاء رابط الدفع

  <CodeGroup>
    ```javascript Node.js / Express theme={null}
    const express = require("express");
    const fetch = require("node-fetch");
    const app = express();

    app.use(express.json());

    // 1. Create order and redirect to payment
    app.post("/checkout", async (req, res) => {
      const { items, total, customerId } = req.body;

      try {
        const orderId = generateOrderId();
        await db.orders.create({
          id: orderId,
          customerId: customerId,
          items: items,
          total: total,
          status: "PENDING",
        });

        const response = await fetch(
          "https://api.oneclickdz.com/v3/ocpay/createLink",
          {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
              "X-Access-Token": process.env.ONECLICK_API_KEY,
            },
            body: JSON.stringify({
              productInfo: {
                title: `Order ${orderId}`,
                amount: total,
              },
              redirectUrl: `https://yoursite.com/orders/${orderId}`,
            }),
          }
        );

        const data = await response.json();

        // IMPORTANT: Save paymentRef with order
        await db.orders.update(orderId, {
          paymentRef: data.data.paymentRef,
        });

        // Redirect to payment page
        res.redirect(data.data.paymentUrl);
      } catch (error) {
        console.error("Checkout error:", error);
        res.status(500).json({ error: "Checkout failed" });
      }
    });

    // 2. Order status page
    app.get("/orders/:orderId", async (req, res) => {
      const { orderId } = req.params;

      try {
        const order = await db.orders.findOne({ id: orderId });

        if (!order) {
          return res.status(404).send("Order not found");
        }

        // Check payment if still pending
        if (order.status === "PENDING" && order.paymentRef) {
          const statusResponse = await fetch(
            `https://api.oneclickdz.com/v3/ocpay/checkPayment/${order.paymentRef}`,
            {
              headers: { "X-Access-Token": process.env.ONECLICK_API_KEY },
            }
          );

          const statusData = await statusResponse.json();

          if (statusData.data.status === "CONFIRMED") {
            await db.orders.update(orderId, { status: "PAID" });
            order.status = "PAID";
          } else if (statusData.data.status === "FAILED") {
            await db.orders.update(orderId, { status: "FAILED" });
            order.status = "FAILED";
          }
        }

        res.render("order-status", { order });
      } catch (error) {
        console.error("Error:", error);
        res.status(500).send("Error loading order");
      }
    });

    // 3. Manual payment check endpoint
    app.post("/orders/:orderId/check", async (req, res) => {
      const { orderId } = req.params;

      try {
        const order = await db.orders.findOne({ id: orderId });

        if (!order || !order.paymentRef) {
          return res.status(404).json({ error: "Order not found" });
        }

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

        const data = await response.json();

        if (data.data.status === "CONFIRMED") {
          await db.orders.update(orderId, { status: "PAID" });
        } else if (data.data.status === "FAILED") {
          await db.orders.update(orderId, { status: "FAILED" });
        }

        res.json({ status: data.data.status });
      } catch (error) {
        console.error("Check error:", error);
        res.status(500).json({ error: "Check failed" });
      }
    });

    function generateOrderId() {
      return `ORD-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
    }

    app.listen(3000);
    ```

    ```python Python / Flask theme={null}
    from flask import Flask, request, redirect, render_template, jsonify
    import requests
    import os
    import time
    import random
    import string

    app = Flask(__name__)

    # 1. Create order and redirect to payment
    @app.route('/checkout', methods=['POST'])
    def checkout():
        data = request.json
        items = data['items']
        total = data['total']
        customer_id = data['customerId']

        try:
            order_id = generate_order_id()
            db.orders.create({
                'id': order_id,
                'customerId': customer_id,
                'items': items,
                'total': total,
                'status': 'PENDING'
            })

            response = requests.post(
                'https://api.oneclickdz.com/v3/ocpay/createLink',
                headers={
                    'Content-Type': 'application/json',
                    'X-Access-Token': os.getenv('ONECLICK_API_KEY')
                },
                json={
                    'productInfo': {
                        'title': f'Order {order_id}',
                        'amount': total
                    },
                    'redirectUrl': f'https://yoursite.com/orders/{order_id}'
                }
            )

            data = response.json()

            # IMPORTANT: Save paymentRef with order
            db.orders.update(order_id, {
                'paymentRef': data['data']['paymentRef']
            })

            return redirect(data['data']['paymentUrl'])

        except Exception as e:
            print(f'Checkout error: {e}')
            return jsonify({'error': 'Checkout failed'}), 500

    # 2. Order status page
    @app.route('/orders/<order_id>')
    def order_status(order_id):
        try:
            order = db.orders.find_one({'id': order_id})

            if not order:
                return 'Order not found', 404

            # Check payment if still pending
            if order['status'] == 'PENDING' and order.get('paymentRef'):
                status_response = requests.get(
                    f"https://api.oneclickdz.com/v3/ocpay/checkPayment/{order['paymentRef']}",
                    headers={'X-Access-Token': os.getenv('ONECLICK_API_KEY')}
                )

                status_data = status_response.json()

                if status_data['data']['status'] == 'CONFIRMED':
                    db.orders.update(order_id, {'status': 'PAID'})
                    order['status'] = 'PAID'
                elif status_data['data']['status'] == 'FAILED':
                    db.orders.update(order_id, {'status': 'FAILED'})
                    order['status'] = 'FAILED'

            return render_template('order_status.html', order=order)

        except Exception as e:
            print(f'Error: {e}')
            return 'Error loading order', 500

    # 3. Manual payment check endpoint
    @app.route('/orders/<order_id>/check', methods=['POST'])
    def check_order(order_id):
        try:
            order = db.orders.find_one({'id': order_id})

            if not order or not order.get('paymentRef'):
                return jsonify({'error': 'Order not found'}), 404

            response = requests.get(
                f"https://api.oneclickdz.com/v3/ocpay/checkPayment/{order['paymentRef']}",
                headers={'X-Access-Token': os.getenv('ONECLICK_API_KEY')}
            )

            data = response.json()

            if data['data']['status'] == 'CONFIRMED':
                db.orders.update(order_id, {'status': 'PAID'})
            elif data['data']['status'] == 'FAILED':
                db.orders.update(order_id, {'status': 'FAILED'})

            return jsonify({'status': data['data']['status']})

        except Exception as e:
            print(f'Check error: {e}')
            return jsonify({'error': 'Check failed'}), 500

    def generate_order_id():
        timestamp = int(time.time())
        random_str = ''.join(random.choices(string.ascii_lowercase + string.digits, k=9))
        return f'ORD-{timestamp}-{random_str}'

    if __name__ == '__main__':
        app.run(port=3000)
    ```

    ```php PHP theme={null}
    <?php
    // checkout.php
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $data = json_decode(file_get_contents('php://input'), true);
        $items = $data['items'];
        $total = $data['total'];
        $customerId = $data['customerId'];

        try {
            $orderId = generateOrderId();
            $db->orders->create([
                'id' => $orderId,
                'customerId' => $customerId,
                'items' => $items,
                'total' => $total,
                'status' => 'PENDING'
            ]);

            $ch = curl_init('https://api.oneclickdz.com/v3/ocpay/createLink');
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_HTTPHEADER, [
                'Content-Type: application/json',
                'X-Access-Token: ' . getenv('ONECLICK_API_KEY')
            ]);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
                'productInfo' => [
                    'title' => "Order $orderId",
                    'amount' => $total
                ],
                'redirectUrl' => "https://yoursite.com/orders.php?id=$orderId"
            ]));

            $response = curl_exec($ch);
            $data = json_decode($response, true);

            // IMPORTANT: Save paymentRef with order
            $db->orders->update($orderId, [
                'paymentRef' => $data['data']['paymentRef']
            ]);

            header('Location: ' . $data['data']['paymentUrl']);
            exit;

        } catch (Exception $e) {
            error_log('Checkout error: ' . $e->getMessage());
            http_response_code(500);
            echo json_encode(['error' => 'Checkout failed']);
        }
    }

    // order-status.php
    $orderId = $_GET['id'] ?? null;

    if (!$orderId) {
        http_response_code(404);
        echo 'Order not found';
        exit;
    }

    try {
        $order = $db->orders->findOne(['id' => $orderId]);

        if (!$order) {
            http_response_code(404);
            echo 'Order not found';
            exit;
        }

        // Check payment if still pending
        if ($order['status'] === 'PENDING' && !empty($order['paymentRef'])) {
            $ch = curl_init(
                "https://api.oneclickdz.com/v3/ocpay/checkPayment/{$order['paymentRef']}"
            );
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_HTTPHEADER, [
                'X-Access-Token: ' . getenv('ONECLICK_API_KEY')
            ]);

            $response = curl_exec($ch);
            $statusData = json_decode($response, true);

            if ($statusData['data']['status'] === 'CONFIRMED') {
                $db->orders->update($orderId, ['status' => 'PAID']);
                $order['status'] = 'PAID';
            } elseif ($statusData['data']['status'] === 'FAILED') {
                $db->orders->update($orderId, ['status' => 'FAILED']);
                $order['status'] = 'FAILED';
            }
        }

        include 'templates/order_status.php';

    } catch (Exception $e) {
        error_log('Error: ' . $e->getMessage());
        http_response_code(500);
        echo 'Error loading order';
    }

    // check-payment.php
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $orderId = $_GET['id'] ?? null;

        try {
            $order = $db->orders->findOne(['id' => $orderId]);

            if (!$order || empty($order['paymentRef'])) {
                http_response_code(404);
                echo json_encode(['error' => 'Order not found']);
                exit;
            }

            $ch = curl_init(
                "https://api.oneclickdz.com/v3/ocpay/checkPayment/{$order['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);

            if ($data['data']['status'] === 'CONFIRMED') {
                $db->orders->update($orderId, ['status' => 'PAID']);
            } elseif ($data['data']['status'] === 'FAILED') {
                $db->orders->update($orderId, ['status' => 'FAILED']);
            }

            echo json_encode(['status' => $data['data']['status']]);

        } catch (Exception $e) {
            error_log('Check error: ' . $e->getMessage());
            http_response_code(500);
            echo json_encode(['error' => 'Check failed']);
        }
    }

    function generateOrderId() {
        $timestamp = time();
        $random = bin2hex(random_bytes(5));
        return "ORD-{$timestamp}-{$random}";
    }
    ?>
    ```
  </CodeGroup>

  ## Frontend: صفحة حالة الطلب

  صفحة HTML بسيطة مع زر التحقق اليدوي:

  ```html theme={null}
  <!DOCTYPE html>
  <html>
    <head>
      <title>Order Status</title>
      <style>
        .status-pending { color: #f59e0b; }
        .status-paid { color: #10b981; }
        .status-failed { color: #ef4444; }
      </style>
    </head>
    <body>
      <h1>Order #<span id="orderId"></span></h1>
      <h2>Status: <span id="status" class="status-pending">PENDING</span></h2>

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

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

      <script>
        const orderId = window.location.pathname.split("/").pop();
        document.getElementById("orderId").textContent = orderId;

        async function checkPayment() {
          const btn = document.getElementById("checkBtn");
          const msg = document.getElementById("message");

          btn.disabled = true;
          msg.textContent = "Checking...";

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

            const data = await response.json();

            const statusEl = document.getElementById("status");
            statusEl.textContent = data.status;
            statusEl.className = `status-${data.status.toLowerCase()}`;

            if (data.status === "CONFIRMED") {
              msg.textContent = "✅ Payment confirmed! Redirecting...";
              setTimeout(() => location.reload(), 2000);
            } else if (data.status === "FAILED") {
              msg.textContent = "❌ Payment failed";
            } else {
              msg.textContent = "⏳ Payment still pending";
            }
          } catch (error) {
            msg.textContent = "Error checking payment";
          } finally {
            btn.disabled = false;
          }
        }

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

  ## مخطط قاعدة البيانات

  مخطط بسيط للطلبات:

  ```sql theme={null}
  CREATE TABLE orders (
    id VARCHAR(50) PRIMARY KEY,
    customer_id VARCHAR(50) NOT NULL,
    total INTEGER NOT NULL,
    status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
    payment_ref VARCHAR(50),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_payment_ref (payment_ref),
    INDEX idx_status (status)
  );
  ```

  ## النقاط الأساسية

  <Warning>
    **مهم:** - احفظ دائماً `paymentRef` مع طلبك - تحقق من حالة الدفع عند عودة العميل - نفّذ الطلبات فقط عند الحالة `CONFIRMED` - تنتهي صلاحية روابط الدفع بعد 20 دقيقة
  </Warning>

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

  <Card title="المتابعة إلى تتبع الحالة" icon="arrow-right" href="/ar/ocpay-guides/3-status-polling">
    تعلّم كيفية أتمتة التحقق من الحالة باستخدام مهام cron
  </Card>
</div>
