الانتقال إلى المحتوى الرئيسي

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.

نظرة عامة

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

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

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

عند إنشاء العميل لطلب، احفظه بحالة PENDING:
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;
}
حرج: يجب أن يكون لكل طلب معرّف فريد ويجب أن يبدأ بحالة PENDING

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

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);

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

صفحة HTML بسيطة مع زر التحقق اليدوي:
<!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>

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

مخطط بسيط للطلبات:
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)
);

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

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

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

المتابعة إلى تتبع الحالة

تعلّم كيفية أتمتة التحقق من الحالة باستخدام مهام cron