Lorsque vous créez une transaction via l’API LigdiCash, vous recevez un token en retour. Ce token n’est pas stable : sa valeur dans le callback diffère de celle obtenue à la création. Si vous vous appuyez sur lui pour identifier vos transactions, vous perdrez le fil entre ce que vous avez initié et ce que vous avez confirmé.
La solution est simple : générez votre propre identifiant côté marchand, stockez-le avant d’appeler l’API, et passez-le dans le champ custom_data. LigdiCash vous le renverra intact dans le callback, et l’affichera dans votre dashboard.
Le problème : le token change entre création et callback
À la création d’une transaction, l’API retourne un token :
{
"response_code": "00",
"token": "abc123-creation",
...
}
Lorsque LigdiCash vous notifie du résultat via le callback, ce même champ contient une valeur différente :
{
"token": "xyz789-callback",
...
}
Les deux tokens désignent la même transaction, mais vous ne pouvez pas les relier directement. Sans identifiant propre à votre système, la réconciliation est impossible.
La solution : injecter votre transaction_id dans custom_data
Le champ custom_data de chaque requête est un objet JSON dont vous définissez librement les clés. C’est ici que vous injectez votre identifiant :
"custom_data": {
"transaction_id": "COMMANDE-20240815-00042"
}
Dans le callback, LigdiCash transforme cet objet en un tableau. Votre transaction_id y apparaît sous cette forme :
"custom_data": [
{
"keyof_customdata": "transaction_id",
"valueof_customdata": "COMMANDE-20240815-00042",
"datecreation_customdata": "2026-02-17 06:56:46.55245"
}
]
Filtrez sur keyof_customdata pour extraire votre valeur — ne vous fiez pas à la position dans le tableau, d’autres entrées peuvent être présentes.
Vous êtes libre de choisir le format qui correspond à votre métier :
| Format | Exemple | Usage typique |
|---|
| Référence commande | ORD-2026-00042 | E-commerce — affiché au client |
| Code court lisible | PAY-8KXZ | Support client, mémorisable |
| UUID v4 | f47ac10b-58cc-4372-a567-0e02b2c3d479 | Systèmes distribués, usage interne |
| Timestamp + aléatoire | 1723718400-a3f9 | File de jobs |
| ID interne | order_8821 | Application existante |
Si vous affichez le transaction_id à vos clients — par exemple sur le reçu ou dans l’interface de suivi — privilégiez un format court et lisible comme ORD-2026-00042. Cela facilite les échanges avec votre support : le client peut le dicter ou le recopier sans erreur.
Si le transaction_id reste purement interne, un UUID v4 est un bon choix par défaut : garanti unique sans coordination entre serveurs.
Évitez les identifiants purement séquentiels exposés côté client (ex : order_1, order_2) — ils révèlent votre volume de transactions. Préférez un préfixe + compteur non trivial, ou un identifiant aléatoire.
Cycle de vie recommandé
Générez le transaction_id
Avant d’appeler l’API, générez votre identifiant unique côté serveur.
Persistez-le en base
Enregistrez-le en base de données avec le statut pending avant d’appeler l’API. Si l’appel échoue, vous avez quand même la trace.
Passez-le dans custom_data
Incluez-le dans le tableau custom_data de votre requête de création.
Stockez le token de création
Conservez également le token retourné par l’API — il sera nécessaire pour appeler l’endpoint confirm lors de la re-vérification du callback.
Retrouvez-le dans le callback
Quand LigdiCash vous notifie, extrayez votre transaction_id depuis custom_data et mettez à jour le statut en base.
Retrouver le transaction_id dans le callback
Le payload du callback contient un champ custom_data. Sa forme varie selon le flux (voir Parser custom_data), mais pour un payin il s’agit d’un tableau :
function extractTransactionId(customData) {
if (!Array.isArray(customData)) return null;
const entry = customData.find(
(item) => item.keyof_customdata === "transaction_id"
);
return entry?.valueof_customdata ?? null;
}
function extractTransactionId(array $customData): ?string {
foreach ($customData as $item) {
if ($item['keyof_customdata'] === 'transaction_id') {
return $item['valueof_customdata'];
}
}
return null;
}
def extract_transaction_id(custom_data: list) -> str | None:
for item in custom_data:
if item.get("keyof_customdata") == "transaction_id":
return item.get("valueof_customdata")
return None
Le callback contient aussi un champ transaction_id à la racine du payload. LigdiCash le construit en concaténant les valueof_customdata de toutes les entrées dont la clé contient id (ex : transaction_id, partner_id, event_id…), séparés par ;. Si vous avez injecté plusieurs champs de ce type, cette valeur racine sera un mélange — pas votre identifiant seul. Préférez toujours l’extraction depuis le tableau custom_data.
Visibilité dans le dashboard LigdiCash
Le transaction_id que vous injectez dans custom_data est affiché dans le dashboard LigdiCash, dans le tableau des transactions — aussi bien pour les payin que pour les payout. Cela vous permet de réconcilier visuellement vos transactions sans avoir à interroger votre propre base de données.
Exemple de requête complet
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": "Abonnement Pro", "price": 5000, "quantity": 1 }],
"total_amount": 5000,
"devise": "XOF",
"description": "Abonnement Pro — Janvier 2025",
"customer": "",
"customer_firstname": "Amadou",
"customer_lastname": "Diallo",
"customer_email": "amadou@exemple.com"
},
"store": {
"name": "MonApp",
"website_url": "https://monapp.com"
},
"actions": {
"cancel_url": "https://monapp.com/paiement/annule",
"return_url": "https://monapp.com/paiement/succes",
"callback_url": "https://monapp.com/api/callback/ligdicash"
},
"custom_data": {
"transaction_id": "COMMANDE-20240815-00042"
}
}
}'
import { randomUUID } from "crypto";
const transactionId = randomUUID(); // ou votre propre format
// Persistez transactionId en base avec statut "pending" avant ce fetch
await db.orders.update({ id: orderId, ligdicash_tx_id: transactionId, status: "pending" });
const response = await fetch(
"https://app.ligdicash.com/pay/v01/redirect/checkout-invoice/create",
{
method: "POST",
headers: {
Apikey: process.env.LIGDICASH_API_KEY,
Authorization: `Bearer ${process.env.LIGDICASH_AUTH_TOKEN}`,
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
commande: {
invoice: {
items: [{ name: "Abonnement Pro", price: 5000, quantity: 1 }],
total_amount: 5000,
devise: "XOF",
description: "Abonnement Pro — Janvier 2025",
customer: "",
customer_firstname: "Amadou",
customer_lastname: "Diallo",
customer_email: "amadou@exemple.com",
},
store: { name: "MonApp", website_url: "https://monapp.com" },
actions: {
cancel_url: "https://monapp.com/paiement/annule",
return_url: "https://monapp.com/paiement/succes",
callback_url: "https://monapp.com/api/callback/ligdicash",
},
custom_data: { transaction_id: transactionId },
},
}),
}
);
const data = await response.json();
// Stockez data.token pour la re-vérification ultérieure
await db.orders.update({ ligdicash_tx_id: transactionId, ligdicash_token: data.token });
Pages associées