Skip to main content

Documentation Index

Fetch the complete documentation index at: https://developers.ligdicash.com/llms.txt

Use this file to discover all available pages before exploring further.

This guide walks through the end-to-end flow for an e-commerce site: from cart validation on the frontend to delivery confirmation on the backend. It uses hosted payin, the simplest integration mode and the one compatible with every operator. Prerequisites: you have a LigdiCash API project with an Apikey and an Auth Token. See Create an API project.

Flow overview

Customer         Frontend          Backend          LigdiCash
  |                 |                  |                 |
  |-- Validate ->   |                  |                 |
  |   cart          |-- POST /pay --> |                  |
  |                 |                  |-- create -----> |
  |                 |                  | <-- token ----- |
  |                 | <-- pay_url ---- |                 |
  |-- Redirect ->   |                  |                 |
  |                 |--- Open URL ---> |                 |
  |  [Payment on the LigdiCash page]   |                 |
  |                 |                  | <-- callback -- |
  |                 |                  |-- confirm ----> |
  |                 |                  | <-- status ---- |
  |                 |                  |-- Confirms      |
  |                 |                  |   order         |
  | <-- Return ---- |                  |                 |
  |   return_url    |-- GET /status -> |                 |
  |                 | <-- result ----- |                 |
Never call the LigdiCash API directly from the frontend. Your Apikey and Auth Token must stay on your backend. See Recommended architecture.

Step 1 — Store the order before payment

Before creating the LigdiCash invoice, persist the order in your database with status pending. You’ll need its identifier to track the payment.
INSERT INTO orders (id, customer_id, amount, status, created_at)
VALUES ('order_7f3a9b', 42, 15000, 'pending', NOW());
Use a non-guessable internal identifier (UUID or prefixed ID) as your transaction_id. You’ll pass it in custom_data and use it to identify the order in the callback.

Step 2 — Create the invoice on the backend

Your backend calls the create-invoice endpoint and stores the returned token.
curl -X POST https://app.ligdicash.com/pay/v01/redirect/checkout-invoice/create \
  -H "Apikey: {API_KEY}" \
  -H "Authorization: Bearer {AUTH_TOKEN}" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "commande": {
      "invoice": {
        "items": [
          {
            "name": "Premium subscription",
            "description": "1 month of Premium access",
            "quantity": 1,
            "unit_price": 15000,
            "total_price": 15000
          }
        ],
        "total_amount": 15000,
        "devise": "XOF",
        "description": "Order #order_7f3a9b",
        "customer": "",
        "customer_firstname": "Aminata",
        "customer_lastname": "Ouedraogo",
        "customer_email": "aminata@example.com",
        "external_id": "",
        "otp": ""
      },
      "store": {
        "name": "My Store",
        "website_url": "https://mystore.com"
      },
      "actions": {
        "cancel_url": "https://mystore.com/payment/cancel",
        "return_url": "https://mystore.com/payment/success",
        "callback_url": "https://mystore.com/api/ligdicash/callback"
      },
      "custom_data": {
        "transaction_id": "order_7f3a9b"
      }
    }
  }'
Success response (response_code: "00"):
{
  "response_code": "00",
  "response_text": "https://app.ligdicash.com/pay/v01/redirect/checkout-invoice/confirm?token=eyJ0...",
  "description": "Checkout-Invoice created with success.",
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
}
Store the token in your database linked to the order. You’ll need it to verify the payment.
UPDATE orders
SET ligdicash_token = 'eyJ0eXAiOiJKV1Qi...'
WHERE id = 'order_7f3a9b';

Step 3 — Redirect the customer to the payment page

Return response_text (the payment URL) to your frontend, then redirect the customer.
Frontend (JavaScript)
// Backend call → returns the payment URL
const { payUrl } = await fetch("/api/orders/order_7f3a9b/pay", {
  method: "POST",
}).then((r) => r.json());

// Redirect in the same tab (recommended)
window.location.href = payUrl;

// Or in a new tab
// window.open(payUrl, "_blank");
Never open the LigdiCash URL in an <iframe>. LigdiCash blocks iframe rendering. Use a same-tab redirect, a new tab, or a popup. See Common pitfalls.
The customer lands on the LigdiCash payment page, picks their mobile money operator, and completes the payment. LigdiCash then redirects them to your return_url (success) or cancel_url (cancellation).

Step 4 — Handle the callback

LigdiCash sends a POST notification to your callback_url whenever the transaction status changes. Your backend must:
  1. Identify the order via custom_data
  2. Re-verify the status using the confirm endpoint (never trust the payload alone)
  3. Update the order in the database
  4. Reply 200 OK
Node.js (Express)
app.post("/api/ligdicash/callback", express.json(), async (req, res) => {
  // Reply 200 immediately to avoid retries
  res.sendStatus(200);

  // Extract transaction_id from custom_data
  const customData = req.body.custom_data ?? [];
  const entry = Array.isArray(customData)
    ? customData.find((e) => e.keyof_customdata === "transaction_id")
    : null;

  if (!entry) return;

  const transactionId = entry.valueof_customdata;

  // Look up the order and its LigdiCash token
  const order = await db.orders.findById(transactionId);
  if (!order || order.status !== "pending") return;

  // Re-verify the status with LigdiCash
  const confirm = await fetch(
    `https://app.ligdicash.com/pay/v01/redirect/checkout-invoice/confirm?token=${order.ligdicash_token}`,
    {
      headers: {
        Apikey: process.env.LIGDICASH_API_KEY,
        Authorization: `Bearer ${process.env.LIGDICASH_AUTH_TOKEN}`,
        Accept: "application/json",
      },
    }
  ).then((r) => r.json());

  if (confirm.status === "completed") {
    await db.orders.update(transactionId, { status: "paid" });
    await delivery.trigger(transactionId);
  } else if (confirm.status === "notcompleted") {
    await db.orders.update(transactionId, { status: "failed" });
  }
  // If "pending": do nothing, wait for the next callback
});
LigdiCash sends two POST requests to your callback for every event: one as application/x-www-form-urlencoded and one as application/json. Handle them idempotently. See Callback idempotency.

Step 5 — Verify the status on the frontend

After the redirect to your return_url, the frontend asks your backend for the final result to display. The return_url is a UI signal — it does not prove the payment succeeded.
Frontend — return_url page
// Read transaction_id from the URL (e.g. /payment/success?order=order_7f3a9b)
const params = new URLSearchParams(window.location.search);
const orderId = params.get("order");

const { status } = await fetch(`/api/orders/${orderId}/status`).then(
  (r) => r.json()
);

switch (status) {
  case "paid":
    showConfirmation();
    break;
  case "pending":
    // The callback hasn't arrived yet — show a loader and poll
    showVerificationWait();
    break;
  case "failed":
    showError();
    break;
}
Backend — GET /api/orders/:id/status
app.get("/api/orders/:id/status", async (req, res) => {
  const order = await db.orders.findById(req.params.id);
  if (!order) return res.status(404).json({ error: "not_found" });

  // If still pending, ask LigdiCash in real time
  if (order.status === "pending") {
    const confirm = await fetch(
      `https://app.ligdicash.com/pay/v01/redirect/checkout-invoice/confirm?token=${order.ligdicash_token}`,
      {
        headers: {
          Apikey: process.env.LIGDICASH_API_KEY,
          Authorization: `Bearer ${process.env.LIGDICASH_AUTH_TOKEN}`,
          Accept: "application/json",
        },
      }
    ).then((r) => r.json());

    if (confirm.status === "completed") {
      await db.orders.update(order.id, { status: "paid" });
      order.status = "paid";
    }
  }

  res.json({ status: order.status });
});

Recap

1

Store the order

Create the order in your database with status pending before any LigdiCash call.
2

Create the invoice

Backend → POST /pay/v01/redirect/checkout-invoice/create. Store the returned token.
3

Redirect the customer

Frontend → redirect to response_text (same tab or popup). Never an iframe.
4

Handle the callback

Reply 200 immediately, then re-verify via confirm with the stored token.
5

Confirm the order

Update the status in your database and trigger delivery if status === "completed".
6

Display the result

Frontend → ask your backend for the real status after returning to return_url.