
API Reference
Baran Business APIs
Les APIs Baran permettent de créer un checkout marchand, recevoir des paiements 1x, autoriser et confirmer des paiements 3x/4x, puis automatiser la réconciliation avec les webhooks.
Base URL
Tous les chemins d’API documentés sont relatifs à l’URL de base suivante.
https://api.getbaran.combaran_pk_... / baran_sec_.... L’environnement est choisi avec X-Baran-Environment. L’API doit être appelée en HTTPS avec TLS 1.2 ou supérieur.Les session_id, checkout_token et authorization_token sont générés avec un générateur cryptographique côté serveur, puis encodés en hexadécimal sur 64 caractères. Le checkout_token est stocké hashé côté Baran, transmis dans le fragment URL du checkout, puis envoyé à l’API via X-Baran-Checkout-Token. Ils expirent après 15 minutes.
Authentification
Les endpoints /api/v1/merchant/* utilisent un Client Key et un Client Secret. Les identifiants sont générés dans le portail marchand, section Intégration. Le secret complet est affiché une seule fois au moment de la génération ou de la rotation.
Content-Type: application/json
X-Baran-Key: baran_pk_VOTRE_CLIENT_KEY
X-Baran-Environment: sandbox
X-Baran-Timestamp: 1781186400
X-Baran-Signature: sha256=...Client Secret dans un frontend, une application mobile, une page HTML publique ou un dépôt Git.signing_key = SHA256(Client Secret) en binaire
signature = "sha256=" + HMAC_SHA256(X-Baran-Timestamp + "." + raw_body, signing_key)La signature est valable 300 secondes. Le Client Secret ne doit jamais être transmis : il sert uniquement à dériver une clé de signature binaire. Les secrets Baran sont générés avec 256 bits d’entropie, ce qui correspond à l’espace effectif de HMAC-SHA256. Pour les webhooks entrants, utilisez le webhook_secret directement, sans cette dérivation.
Une signature valide est consommable une seule fois pendant la fenêtre de 300 secondes. Un rejeu exact retourne 409 replayed_signature.
Les nonces expirés sont purgés par le cron cleanup_merchant_api_logs.php, afin d’éviter une croissance non bornée de la table anti-rejeu.
Checkout hébergé Baran
Le checkout client doit rester exclusivement hébergé par Baran. Le marchand crée une session checkout côté serveur, puis redirige le client vers la page Baran avec le session_id.
code_secret, héberger sa propre page PIN, proxyfier le formulaire de paiement, ni placer le Client Secret dans un frontend.Les endpoints publics /api/checkout/authorize et /api/checkout/pay-one-shot sont réservés à la page checkout Baran. Les requêtes navigateur provenant d’une origine non-Baran sont refusées.
Le paiement 1x applique un throttling par IP, session et téléphone hashé, puis retourne des erreurs génériques afin de limiter l’énumération de comptes.
return_url HTTPS publique peut être utilisée même si aucun site marchand n’est encore déclaré. En production, son domaine doit correspondre au domaine website validé dans le profil marchand.Créer une session checkout
Crée une session checkout hébergée Baran valable 15 minutes.
Authentification : Client Key, X-Baran-Environment, X-Baran-Timestamp et X-Baran-Signature obligatoires. Le Client Secret ne doit jamais être transmis.
Paramètres
| Champ | Type | Présence | Description |
|---|---|---|---|
| order_id | string | Obligatoire | Référence unique de la commande côté marchand. |
| amount | number | Obligatoire | Montant en FCFA. Doit respecter les limites Alma du backend. |
| return_url | string | Obligatoire | URL HTTPS de retour vers le site marchand après paiement. En production, le domaine doit correspondre au site déclaré du marchand. |
| order_key | string | Optionnel | Clé technique marchand, par exemple une clé WooCommerce. |
curl -X POST https://api.getbaran.com/api/v1/merchant/checkout-sessions \
-H "Content-Type: application/json" \
-H "X-Baran-Key: baran_pk_VOTRE_CLIENT_KEY" \
-H "X-Baran-Environment: sandbox" \
-H "X-Baran-Timestamp: 1781186400" \
-H "X-Baran-Signature: sha256=SIGNATURE_DU_BODY" \
-d '{
"order_id": "CMD-2026-0001",
"order_key": "wc_order_9f8a7b",
"amount": 50000,
"return_url": "https://boutique.example/merci"
}'<?php
$payload = [
'order_id' => 'CMD-2026-0001',
'order_key' => 'wc_order_9f8a7b',
'amount' => 50000,
'return_url' => 'https://boutique.example/merci',
];
$body = json_encode($payload, JSON_UNESCAPED_SLASHES);
$timestamp = (string) time();
$signingKey = hash('sha256', 'baran_sec_VOTRE_CLIENT_SECRET', true);
$signature = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, $signingKey);
$ch = curl_init('https://api.getbaran.com/api/v1/merchant/checkout-sessions');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-Baran-Key: baran_pk_VOTRE_CLIENT_KEY',
'X-Baran-Environment: sandbox',
'X-Baran-Timestamp: ' . $timestamp,
'X-Baran-Signature: ' . $signature,
],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);const payload = {
order_id: 'CMD-2026-0001',
order_key: 'wc_order_9f8a7b',
amount: 50000,
return_url: 'https://boutique.example/merci',
};
const body = JSON.stringify(payload);
const timestamp = Math.floor(Date.now() / 1000).toString();
// signWithClientSecret doit dériver SHA-256(Client Secret) en binaire, puis signer timestamp + "." + body.
const signature = signWithClientSecret(timestamp + '.' + body, 'baran_sec_VOTRE_CLIENT_SECRET');
const response = await fetch('https://api.getbaran.com/api/v1/merchant/checkout-sessions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Baran-Key': 'baran_pk_VOTRE_CLIENT_KEY',
'X-Baran-Environment': 'sandbox',
'X-Baran-Timestamp': timestamp,
'X-Baran-Signature': signature,
},
body,
});
const data = await response.json();{
"status": "success",
"data": {
"checkout_session_id": "8b34e5a0b8f68c7d7f90868e4f6f5a7e89ab12cd34ef56ab78cd90ef12ab34cd",
"checkout_token": "f3c0c5e9a1b24e6b7c8d9e0f11223344556677889900aabbccddeeff00112233",
"expires_in": 900
}
}Erreurs possibles
| HTTP | Message | Action recommandée |
|---|---|---|
| 400 | Missing required fields: order_id, amount, return_url | Ajouter les champs obligatoires. |
| 400 | Invalid amount | Vérifier le montant et les limites autorisées. |
| 400 | invalid_return_url | Utiliser une URL HTTPS appartenant au domaine déclaré du marchand. |
| 401 | Invalid API credentials | Vérifier le Client Key, le timestamp et la signature. |
| 403 | production_not_approved | Demander ou attendre la validation production admin. |
| 409 | replayed_signature | Regénérer timestamp/signature et ne pas rejouer une requête déjà consommée. |
| 405 | Méthode non autorisée | Utiliser POST. |
Récupérer une session checkout
Retourne les informations publiques d’une session checkout non expirée.
Authentification : session_id + header X-Baran-Checkout-Token. Endpoint consommé par la page checkout Baran.
Paramètres
| Champ | Type | Présence | Description |
|---|---|---|---|
| session_id | string | Obligatoire | Identifiant hexadécimal de 64 caractères dans l’URL. |
| X-Baran-Checkout-Token | header | Obligatoire | Token hexadécimal de 64 caractères transmis à la page checkout Baran. |
curl "https://api.getbaran.com/api/checkout/session/8b34e5a0b8f68c7d7f90868e4f6f5a7e89ab12cd34ef56ab78cd90ef12ab34cd" \
-H "X-Baran-Checkout-Token: f3c0c5e9a1b24e6b7c8d9e0f11223344556677889900aabbccddeeff00112233"<?php
$sessionId = '8b34e5a0b8f68c7d7f90868e4f6f5a7e89ab12cd34ef56ab78cd90ef12ab34cd';
$checkoutToken = 'f3c0c5e9a1b24e6b7c8d9e0f11223344556677889900aabbccddeeff00112233';
$context = stream_context_create(['http' => ['header' => 'X-Baran-Checkout-Token: ' . $checkoutToken]]);
$response = file_get_contents('https://api.getbaran.com/api/checkout/session/' . $sessionId, false, $context);
$data = json_decode($response, true);const sessionId = '8b34e5a0b8f68c7d7f90868e4f6f5a7e89ab12cd34ef56ab78cd90ef12ab34cd';
const checkoutToken = 'f3c0c5e9a1b24e6b7c8d9e0f11223344556677889900aabbccddeeff00112233';
const response = await fetch(`https://api.getbaran.com/api/checkout/session/${sessionId}`, {
headers: { 'X-Baran-Checkout-Token': checkoutToken },
});
const data = await response.json();{
"status": "success",
"order_id": "CMD-2026-0001",
"order_key": "wc_order_9f8a7b",
"amount": 50000,
"return_url": "https://boutique.example/merci",
"currency": "XOF"
}Erreurs possibles
| HTTP | Message | Action recommandée |
|---|---|---|
| 400 | Invalid session_id | Vérifier que `session_id` contient 64 caractères hexadécimaux. |
| 404 | Session expired or not found | Créer une nouvelle session checkout. |
| 405 | Méthode non autorisée | Utiliser GET. |
Paiement 1x hébergé
Le paiement 1x est exécuté uniquement depuis la page checkout hébergée par Baran.
Authentification : Endpoint interne checkout Baran. Il ne doit pas être appelé par une boutique, un plugin ou un proxy marchand.
Paramètres
| Champ | Type | Présence | Description |
|---|---|---|---|
| session_id | string | Interne | Identifiant de session transmis à la page checkout Baran. |
| checkout_token | string | Interne | Token de session transmis à la page checkout Baran. |
| phone | string | Interne | Téléphone saisi par le client sur Baran. |
| code_secret | string | Interne | Code secret saisi uniquement sur Baran. Jamais visible par le marchand. |
# Non applicable côté marchand.
# Le marchand redirige le client vers la page checkout Baran avec le session_id.<?php
// Ne collectez jamais le code_secret client.
// Créez une session checkout, puis redirigez vers l'URL Baran retournée/configurée.// Ne pas appeler /api/checkout/pay-one-shot depuis une boutique.
// Ce flux est réservé à la page checkout hébergée par Baran.{
"status": "success",
"transaction_id": "txn_7f4c9d2a8b1e6f30",
"order_id": "CMD-2026-0001",
"amount": 50000,
"amount_paid": 49500,
"commission": 500,
"payment_method": "1x",
"return_url": "https://boutique.example/merci"
}Erreurs possibles
| HTTP | Message | Action recommandée |
|---|---|---|
| 400 | Missing session_id, checkout_token, phone or code_secret | Erreur interne checkout Baran : relancer la session. |
| 400 | Invalid secret code | Erreur interne checkout Baran : format PIN invalide. |
| 401 | Paiement refusé | Le client doit vérifier ses informations sur la page Baran. |
| 429 | Trop de tentatives | Attendre la fin du blocage temporaire. |
| 409 | Order already paid | Traiter la commande comme déjà réglée. |
| 500 | Erreur serveur lors du paiement 1x | Réessayer ou contacter le support. |
Confirmer un paiement 3x/4x
Finalise un paiement fractionné à partir d’un token d’autorisation.
Authentification : Client Key, X-Baran-Environment, X-Baran-Timestamp et X-Baran-Signature obligatoires. Le Client Secret ne doit jamais être transmis.
Paramètres
| Champ | Type | Présence | Description |
|---|---|---|---|
| authorization_token | string | Obligatoire | Token hexadécimal de 64 caractères obtenu via `authorize`. |
| order_id | string | Obligatoire | Même `order_id` que celui envoyé à l’autorisation. |
curl -X POST https://api.getbaran.com/api/v1/merchant/installments/confirm \
-H "Content-Type: application/json" \
-H "X-Baran-Key: baran_pk_VOTRE_CLIENT_KEY" \
-H "X-Baran-Environment: sandbox" \
-H "X-Baran-Timestamp: 1781186400" \
-H "X-Baran-Signature: sha256=SIGNATURE_DU_BODY" \
-d '{
"authorization_token": "9e4c4f3d1b2a5c6d7e8f90123456789abcdef123456789abcdef123456789abcd",
"order_id": "CMD-2026-0001"
}'<?php
$payload = [
'authorization_token' => '9e4c4f3d1b2a5c6d7e8f90123456789abcdef123456789abcdef123456789abcd',
'order_id' => 'CMD-2026-0001',
];const payload = {
authorization_token: '9e4c4f3d1b2a5c6d7e8f90123456789abcdef123456789abcdef123456789abcd',
order_id: 'CMD-2026-0001',
};
const body = JSON.stringify(payload);
const timestamp = Math.floor(Date.now() / 1000).toString();
// signWithClientSecret doit dériver SHA-256(Client Secret) en binaire, puis signer timestamp + "." + body.
const signature = signWithClientSecret(timestamp + '.' + body, 'baran_sec_VOTRE_CLIENT_SECRET');
const response = await fetch('https://api.getbaran.com/api/v1/merchant/installments/confirm', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Baran-Key': 'baran_pk_VOTRE_CLIENT_KEY',
'X-Baran-Environment': 'sandbox',
'X-Baran-Timestamp': timestamp,
'X-Baran-Signature': signature,
},
body,
});{
"status": "success",
"data": {
"status": "completed",
"transaction_id": "txn_7f4c9d2a8b1e6f30",
"order_id": "CMD-2026-0001",
"amount": 50000,
"amount_paid": 49000,
"commission": 1000,
"installment_count": 4,
"paid_at": "2026-06-12T00:00:00+00:00"
}
}Erreurs possibles
| HTTP | Message | Action recommandée |
|---|---|---|
| 400 | Missing or invalid authorization_token | Utiliser le token retourné par `authorize`. |
| 400 | authorization_expired_or_used | Relancer une autorisation. |
| 400 | order_id does not match authorization | Envoyer le même `order_id` que lors de l’autorisation. |
| 403 | Token does not belong to this merchant | Vérifier les identifiants API utilisés. |
| 409 | Duplicate order_id | Ne pas confirmer deux fois la même commande. |
| 500 | Transaction could not be completed | Vérifier l’état de la commande avant de réessayer. |
Simuler un événement sandbox
Crée une delivery webhook de test sans débit ni crédit réel.
Authentification : Client Key, X-Baran-Environment: sandbox, X-Baran-Timestamp et X-Baran-Signature obligatoires. Le Client Secret ne doit jamais être transmis.
Paramètres
| Champ | Type | Présence | Description |
|---|---|---|---|
| event | string | Optionnel | Valeurs : `payment.completed`, `payment.failed`, `webhook.test`. |
| order_id | string | Optionnel | Référence de test. Générée automatiquement si absente. |
| amount | number | Optionnel | Montant simulé en FCFA. |
curl -X POST https://api.getbaran.com/api/v1/merchant/sandbox/simulate \
-H "Content-Type: application/json" \
-H "X-Baran-Key: baran_pk_VOTRE_CLIENT_KEY" \
-H "X-Baran-Environment: sandbox" \
-H "X-Baran-Timestamp: 1781186400" \
-H "X-Baran-Signature: sha256=SIGNATURE_DU_BODY" \
-d '{
"event": "payment.completed",
"order_id": "SIM-2026-0001",
"amount": 50000
}'<?php
$payload = [
'event' => 'payment.completed',
'order_id' => 'SIM-2026-0001',
'amount' => 50000,
];const payload = {
event: 'payment.completed',
order_id: 'SIM-2026-0001',
amount: 50000,
};
const body = JSON.stringify(payload);
const timestamp = Math.floor(Date.now() / 1000).toString();
// signWithClientSecret doit dériver SHA-256(Client Secret) en binaire, puis signer timestamp + "." + body.
const signature = signWithClientSecret(timestamp + '.' + body, 'baran_sec_VOTRE_CLIENT_SECRET');
const response = await fetch('https://api.getbaran.com/api/v1/merchant/sandbox/simulate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Baran-Key': 'baran_pk_VOTRE_CLIENT_KEY',
'X-Baran-Environment': 'sandbox',
'X-Baran-Timestamp': timestamp,
'X-Baran-Signature': signature,
},
body,
});{
"status": "success",
"data": {
"status": "queued",
"event": "payment.completed",
"delivery_id": "whd_9c1e7a4f2b8d03aa"
}
}Erreurs possibles
| HTTP | Message | Action recommandée |
|---|---|---|
| 400 | invalid_event | Utiliser un événement supporté. |
| 401 | Invalid API credentials | Vérifier les identifiants API. |
| 403 | sandbox_only | Envoyer `X-Baran-Environment: sandbox`. |
Webhooks
Baran envoie un webhook lorsqu’un paiement est complété si une URL webhook est configurée dans le portail marchand. Votre endpoint doit répondre avec un code HTTP `2xx` en moins de 5 secondes.
Content-Type: application/json
User-Agent: Baran-Webhook/1.0
X-Baran-Delivery: whd_9c1e7a4f2b8d03aa
X-Baran-Timestamp: 1781186400
X-Baran-Signature: sha256=1f2a...{
"event": "payment.completed",
"transaction_id": "txn_7f4c9d2a8b1e6f30",
"order_id": "CMD-2026-0001",
"amount": 50000,
"amount_paid": 49000,
"status": "completed",
"payment_method": "1x",
"timestamp": "2026-06-12T00:00:00+00:00"
}Le webhook_secret est déjà une clé HMAC dédiée aux webhooks. Ne lui appliquez pas la dérivation SHA256(Client Secret) utilisée pour signer les requêtes API marchandes sortantes.
<?php
$payload = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_BARAN_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_BARAN_SIGNATURE'] ?? '';
$webhookSecret = getenv('BARAN_WEBHOOK_SECRET');
$now = time();
if (!preg_match('/^\d{10}$/', $timestamp)) {
http_response_code(401);
exit('invalid timestamp');
}
$timestampValue = (int) $timestamp;
if ($timestampValue < ($now - 300) || $timestampValue > ($now + 300)) {
http_response_code(401);
exit('stale webhook');
}
$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $payload, $webhookSecret);
if (!hash_equals($expected, $signature)) {
http_response_code(401);
exit('invalid signature');
}
$event = json_decode($payload, true);
if (($event['event'] ?? '') === 'payment.completed') {
// Idempotence : vérifier transaction_id + order_id avant de valider la commande.
}
http_response_code(200);
echo json_encode(['received' => true]);<?php
function verifyBaranWebhook(string $payload, string $timestamp, string $signature, string $secret): bool
{
if (!preg_match('/^\d{10}$/', $timestamp)) {
return false;
}
$timestampValue = (int) $timestamp;
$now = time();
if ($timestampValue < ($now - 300) || $timestampValue > ($now + 300)) {
return false;
}
$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $payload, $secret);
return hash_equals($expected, $signature);
}Votre handler webhook doit être idempotent : stockez X-Baran-Delivery et/ou vérifiez transaction_id + order_id avant de valider une commande. Si le même delivery est reçu à nouveau, retournez 200 sans retraiter la commande.
Le webhook_secret est distinct du Client Secret. Une rotation s’applique aux nouvelles deliveries ; les deliveries déjà en file conservent le secret utilisé à leur création. Pendant la transition, votre endpoint peut accepter temporairement l’ancien et le nouveau secret, puis retirer l’ancien une fois les anciennes deliveries traitées.
Codes d’erreur
| HTTP | Message | Action recommandée |
|---|---|---|
| 400 | Requête invalide | Corriger le JSON, les champs requis, le format du téléphone, le montant ou le nombre d’échéances. |
| 401 | Non authentifié | Vérifier `X-Baran-Key`, `X-Baran-Timestamp`, `X-Baran-Signature` ou la signature webhook. |
| 403 | Accès refusé | Vérifier le statut marchand, l’environnement demandé, la validation production ou l’IP whitelist obligatoire en production. |
| 404 | Ressource introuvable | Vérifier `session_id`, expiration de session ou URL appelée. |
| 405 | Méthode non autorisée | Utiliser la méthode HTTP documentée pour l’endpoint. |
| 409 | Conflit | Traiter la commande comme déjà payée, utiliser un nouvel `order_id`, ou régénérer une signature si `replayed_signature` est retourné. |
| 429 | Rate limit | Réessayer plus tard avec backoff exponentiel. |
| 500 | Erreur serveur | Réessayer, journaliser `order_id`, puis contacter le support si l’erreur persiste. |
Guide de test sandbox
Données de test
- Utiliser
X-Baran-Environment: sandbox. - Utiliser un `order_id` unique par scénario.
- Utiliser un compte client Baran existant pour tester l’éligibilité.
- Configurer une URL webhook HTTPS publique.
Cas à valider
- Session checkout créée puis récupérée.
- Session expirée ou `session_id` invalide.
- Paiement 1x avec code secret invalide.
- Paiement 3x/4x client non éligible.
- Confirmation répétée du même `authorization_token`.
- Webhook signé valide, signature invalide et timestamp expiré.
Avant le passage en production, demandez la validation production dans le portail marchand. Après approbation admin, gardez les mêmes identifiants et envoyez X-Baran-Environment: production. Vérifiez que votre serveur ne journalise jamais le Client Secret complet.