Passer au contenu principal

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.

Vue d’ensemble

Le flux de paiement est simple :
  1. Le client crée une commande → Sauvegardez comme PENDING
  2. Créez un lien de paiement → Obtenez paymentRef
  3. Sauvegardez paymentRef avec la commande (important !)
  4. Redirigez le client vers la page de paiement
  5. Le client paie et revient
  6. Vérifiez le statut du paiement et mettez à jour la commande

Implémentation complète

Étape 1 : Création de la commande

Lorsqu’un client passe une commande, sauvegardez-la avec le statut PENDING :
async function createOrder(customerData, items, total) {
  // Générer un ID de commande unique
  const orderId = generateUniqueId(); // ex. "ORD-2025-001234"

  // Sauvegarder la commande en base de données
  const order = await db.orders.create({
    id: orderId,
    customerId: customerData.id,
    items: items,
    totalAmount: total,
    status: "PENDING", // Important : commencer par PENDING
    paymentRef: null, // Sera défini après la création du lien de paiement
    createdAt: new Date(),
    updatedAt: new Date(),
  });

  return order;
}
Critique : Chaque commande doit avoir un ID unique et doit commencer avec le statut PENDING

Étape 2 : Créer un lien de paiement

const express = require("express");
const fetch = require("node-fetch");
const app = express();

app.use(express.json());

// 1. Créer la commande et rediriger vers le paiement
app.post("/checkout", async (req, res) => {
  const { items, total, customerId } = req.body;

  try {
    // Créer la commande en base de données
    const orderId = generateOrderId();
    await db.orders.create({
      id: orderId,
      customerId: customerId,
      items: items,
      total: total,
      status: "PENDING",
    });

    // Créer le lien de paiement
    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 : Sauvegarder paymentRef avec la commande
    await db.orders.update(orderId, {
      paymentRef: data.data.paymentRef,
    });

    // Rediriger vers la page de paiement
    res.redirect(data.data.paymentUrl);
  } catch (error) {
    console.error("Checkout error:", error);
    res.status(500).json({ error: "Checkout failed" });
  }
});

// 2. Page de statut de commande
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. Endpoint de vérification manuelle du paiement
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();

    // Mettre à jour le statut de la commande
    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 : Page de statut de commande

Page HTML simple avec bouton de vérification manuelle :
<!DOCTYPE html>
<html>
  <head>
    <title>Statut de la commande</title>
    <style>
      .status-pending { color: #f59e0b; }
      .status-paid { color: #10b981; }
      .status-failed { color: #ef4444; }
    </style>
  </head>
  <body>
    <h1>Commande #<span id="orderId"></span></h1>
    <h2>Statut : <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 = "Vérification...";

        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 = "✅ Paiement confirmé ! Actualisation...";
            setTimeout(() => location.reload(), 2000);
          } else if (data.status === "FAILED") {
            msg.textContent = "❌ Paiement échoué";
          } else {
            msg.textContent = "⏳ Paiement toujours en attente";
          }
        } catch (error) {
          msg.textContent = "Erreur lors de la vérification";
        } finally {
          btn.disabled = false;
        }
      }

      // Vérification automatique au chargement de la page
      if (document.getElementById("status").textContent === "PENDING") {
        checkPayment();
      }
    </script>
  </body>
</html>

Schéma de base de données

Schéma simple pour les commandes :
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)
);

Points clés

Important : - Sauvegardez toujours paymentRef avec votre commande - Vérifiez le statut du paiement au retour du client - Ne traitez les commandes qu’avec le statut CONFIRMED - Les liens de paiement expirent après 20 minutes

Prochaines étapes

Continuer vers le suivi du statut

Apprenez à automatiser la vérification du statut avec des tâches cron