Un endpoint serveur pour créer l’intent, puis une page de paiement hébergée par lahsab qui affiche vos coordonnées et récolte la preuve. Base : https://api.lahsab.com. Tous les montants sont des entiers en dinars (DZD). L’environnement est déduit du préfixe de la clé (lsk_sandbox_ / lsk_live_).
Créer un intent
/payments/intents Crée une intention d’encaissement et renvoie un checkoutUrl — le lien de paiement hébergé, valable 48 h. Redirigez l’acheteur dessus.
| Champ | Type | Requis | Détail |
|---|---|---|---|
amount | int | Oui | Entier strictement positif (DZD). |
customerRef | string | Non | Votre référence (ex. identifiant utilisateur). |
method | string | Non | ccp ou baridimob (indicatif). |
metadata | object | Non | Objet JSON libre. |
En production, les coordonnées de réception (titulaire + RIP) doivent être renseignées, sinon 409 MERCHANT_PAYMENT_DETAILS_MISSING. La sandbox n’a pas cette contrainte.
Erreurs : 400 VALIDATION_ERROR, 401 AUTH_UNAUTHORIZED, 409 MERCHANT_PAYMENT_DETAILS_MISSING.
curl -X POST https://api.lahsab.com/payments/intents \
-H "x-api-key: lsk_live_CLE" \
-H "content-type: application/json" \
-d '{
"amount": 2500,
"customerRef": "user_abc123",
"metadata": { "orderId": "CMD-1042" }
}' const res = await fetch("https://api.lahsab.com/payments/intents", {
method: "POST",
headers: {
"x-api-key": process.env.LAHSAB_API_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
amount: 2500,
customerRef: "user_abc123",
metadata: { orderId: "CMD-1042" },
}),
});
const intent = await res.json();
window.location.href = intent.checkoutUrl; <?php
$ch = curl_init("https://api.lahsab.com/payments/intents");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"x-api-key: " . getenv("LAHSAB_API_KEY"),
"content-type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"amount" => 2500,
"customerRef" => "user_abc123",
"metadata" => ["orderId" => "CMD-1042"],
]),
]);
$intent = json_decode(curl_exec($ch), true); import os, requests
res = requests.post(
"https://api.lahsab.com/payments/intents",
headers={"x-api-key": os.environ["LAHSAB_API_KEY"]},
json={
"amount": 2500,
"customerRef": "user_abc123",
"metadata": {"orderId": "CMD-1042"},
},
)
intent = res.json() Réponse 201
{
"id": "a7f3c2e1-9b4d-4e6a-8c1f-2d3e4f5a6b7c",
"environment": "live",
"customerRef": "user_abc123",
"amount": 2500,
"status": "pending",
"checkoutUrl": "https://app.lahsab.com/pay/a7f3c2e1-9b4d-4e6a-8c1f-2d3e4f5a6b7c",
"metadata": { "orderId": "CMD-1042" },
"expiresAt": "2026-07-13T10:00:00.000Z",
"proofs": [],
"createdAt": "2026-07-11T10:00:00.000Z"
} Consulter un intent
/payments/intents/:id Retourne l’état courant, si l’intent appartient à la clé et à son environnement. Sinon 404 — un intent d’un autre environnement est invisible.
Erreurs : 401 AUTH_UNAUTHORIZED, 404 PAYMENT_INTENT_NOT_FOUND.
curl https://api.lahsab.com/payments/intents/INTENT_ID \
-H "x-api-key: lsk_live_CLE" const res = await fetch(
"https://api.lahsab.com/payments/intents/" + intentId,
{ headers: { "x-api-key": process.env.LAHSAB_API_KEY } },
);
const intent = await res.json(); <?php
$ch = curl_init("https://api.lahsab.com/payments/intents/" . $intentId);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["x-api-key: " . getenv("LAHSAB_API_KEY")],
]);
$intent = json_decode(curl_exec($ch), true); import os, requests
res = requests.get(
"https://api.lahsab.com/payments/intents/" + intent_id,
headers={"x-api-key": os.environ["LAHSAB_API_KEY"]},
)
intent = res.json() Réponse 200
{
"id": "a7f3c2e1-9b4d-4e6a-8c1f-2d3e4f5a6b7c",
"environment": "live",
"amount": 2500,
"status": "proof_submitted",
"checkoutUrl": "https://app.lahsab.com/pay/a7f3c2e1-9b4d-4e6a-8c1f-2d3e4f5a6b7c",
"expiresAt": "2026-07-13T10:00:00.000Z",
"proofs": [
{
"id": "9c8b7a6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
"originalName": "recu-baridimob.jpg",
"mimeType": "image/jpeg",
"size": 184230,
"submittedAt": "2026-07-11T10:12:00.000Z"
}
],
"createdAt": "2026-07-11T10:00:00.000Z"
} Page de paiement
/payments/checkout/:id Aucune authentification — c’est ce que sert la page hébergée au navigateur de l’acheteur. Renvoie une vue publique de l’intent : le montant, vos coordonnées de réception, l’état. Jamais de clé API, de secret webhook, ni de metadata.
Un intent expiré (au-delà de 48 h) est renvoyé avec status: "expired".
Erreurs : 404 PAYMENT_INTENT_NOT_FOUND.
curl https://api.lahsab.com/payments/checkout/INTENT_ID const res = await fetch(
"https://api.lahsab.com/payments/checkout/" + intentId,
);
const checkout = await res.json(); <?php
$ch = curl_init("https://api.lahsab.com/payments/checkout/" . $intentId);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$checkout = json_decode(curl_exec($ch), true); import requests
res = requests.get(
"https://api.lahsab.com/payments/checkout/" + intent_id,
)
checkout = res.json() Réponse 200
{
"id": "a7f3c2e1-9b4d-4e6a-8c1f-2d3e4f5a6b7c",
"environment": "live",
"amount": 2500,
"method": "baridimob",
"status": "pending",
"expiresAt": "2026-07-13T10:00:00.000Z",
"hasProof": false,
"merchantConfigured": true,
"merchant": {
"displayName": "ACME Store",
"receivingHolder": "SARL ACME",
"receivingCcpNumber": "0123456789",
"receivingCcpKey": "12",
"receivingRip": "00799999012345678912",
"receivingBaridimobPhone": "0770000000",
"paymentInstructions": "Virement BaridiMob, puis téléversez la capture.",
"supportEmail": "support@acme.dz",
"supportPhone": "0770000000"
}
} Déposer une preuve
/payments/intents/:id/proof Aucune authentification — endpoint public, utilisé par la page de paiement (ou votre propre page si vous en construisez une). Envoi multipart/form-data, champ file. L’intent passe en proof_submitted.
Erreurs : 400 PAYMENT_PROOF_FILE_MISSING, 404 PAYMENT_INTENT_NOT_FOUND, 409 PAYMENT_INVALID_TRANSITION, 409 PAYMENT_INTENT_EXPIRED, 413 FILE_TOO_LARGE.
curl -X POST https://api.lahsab.com/payments/intents/INTENT_ID/proof \
-F "file=@recu-baridimob.jpg" const form = new FormData();
form.append("file", fileBlob, "recu.jpg");
const res = await fetch(
"https://api.lahsab.com/payments/intents/" + intentId + "/proof",
{ method: "POST", body: form },
); <?php
$ch = curl_init("https://api.lahsab.com/payments/intents/" . $intentId . "/proof");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => ["file" => new CURLFile("recu.jpg")],
]);
$intent = json_decode(curl_exec($ch), true); import requests
with open("recu.jpg", "rb") as f:
res = requests.post(
"https://api.lahsab.com/payments/intents/" + intent_id + "/proof",
files={"file": f},
) Réponse 201 — l’intent mis à jour, status à proof_submitted.
Enveloppe d’erreur
Toutes les erreurs partagent la même forme. Testez l’errorCode (stable), pas le message.
{
"statusCode": 404,
"errorCode": "PAYMENT_INTENT_NOT_FOUND",
"message": "Payment intent not found",
"timestamp": "2026-07-11T10:00:00.000Z",
"path": "/payments/intents/a7f3c2e1-9b4d-4e6a-8c1f-2d3e4f5a6b7c"
}
errorCode | Statut | Signification |
|---|---|---|
VALIDATION_ERROR | 400 | Corps de requête invalide. |
PAYMENT_PROOF_FILE_MISSING | 400 | Aucun fichier de preuve. |
AUTH_UNAUTHORIZED | 401 | Clé API manquante ou invalide. |
PAYMENT_INTENT_NOT_FOUND | 404 | Intent inconnu, d’un autre marchand ou environnement. |
PAYMENT_INVALID_TRANSITION | 409 | Transition d’état impossible. |
PAYMENT_INTENT_EXPIRED | 409 | Lien de paiement expiré (au-delà de 48 h). |
MERCHANT_PAYMENT_DETAILS_MISSING | 409 | Coordonnées de réception requises avant la production. |
FILE_TOO_LARGE | 413 | Fichier de preuve trop volumineux. |
RATE_LIMITED | 429 | Trop de requêtes. |