SDK PHP
xaalis-php (dans integrations/sdk-php) encapsule l’API pour les serveurs PHP 8.1+. Aucune dépendance à l’exécution
en dehors de ext-curl et ext-json. Il n’est pas encore publié sur Packagist : utilisez-le depuis le monorepo, via
l’autoload PSR-4 de Composer (Xaalis\ → src/) ou le fichier autoload.php fourni.
require __DIR__ . '/vendor/autoload.php';
$xaalis = new Xaalis\Client(getenv('XAALIS_SECRET_KEY'), ['base_url' => 'http://localhost:4000']);
$payment = $xaalis->payments->create( [ 'amount' => 15000, // int, XOF — jamais un float ni une chaîne 'description' => 'Commande #1042', 'client_reference' => 'order_1042', 'success_url' => 'https://shop.example/merci', 'metadata' => ['order_id' => '1042'], ], ['idempotency_key' => 'payment:order_1042'],);
header('Location: ' . $payment['checkout_url']); // page de paiement hébergéeLes réponses sont des tableaux associatifs avec les mêmes champs que l’API (objet paiement).
| Service | Méthodes |
|---|---|
payments |
create, retrieve, list, all (générateur sur toutes les pages) |
payouts |
create, retrieve, list, all |
balance |
retrieve |
account |
retrieve, update(['webhook_url' => 'https://…']) |
webhookDeliveries |
list(['limit' => 20]) |
testHelpers |
simulatePayment($id, 'succeeded' | 'failed') — clés de test uniquement |
Xaalis\Webhook::constructEvent($raw, $header, $secret) |
vérifie et décode un webhook |
| Option | Valeur par défaut | |
|---|---|---|
base_url |
https://api.xaalis.sn |
une clé de production sur http:// est refusée |
timeout |
30 |
secondes par tentative |
max_retries |
2 |
nouvelles tentatives après la première |
http |
curl | fn($method, $url, $headers, $body, $timeout) qui renvoie ['status', 'body', 'headers'] — pour les tests |
La clé doit être de la forme sk_test_… / sk_live_… (40 caractères après le préfixe) ; elle n’apparaît jamais dans
les messages d’exception ni dans var_dump($xaalis). $xaalis->testMode vaut true pour les clés de test.
Ce qu’il fait pour vous
Section intitulée « Ce qu’il fait pour vous »- Idempotence : chaque
createenvoie uneIdempotency-Key— un UUID v4 aléatoire si vous ne passez pasidempotency_key— et la réutilise à chaque nouvelle tentative. Passez la vôtre (votre numéro de commande) pour rester protégé même en cas de redémarrage du processus. Voir Idempotence. - Nouvelles tentatives : erreurs réseau,
429et5xx, avec un délai exponentiel et prise en compte deRetry-After— uniquement quand rejouer la requête est sans risque :GET,PATCH, ou une création accompagnée de sa clé. Un4xxn’est jamais relancé. - Entiers uniquement :
amountdoit être unintPHP ;150.5,15000.0et"15000"lèvent\InvalidArgumentExceptionavant tout envoi.
use Xaalis\Exception\ApiException;use Xaalis\Exception\ConnectionException;
try { $xaalis->payouts->create(['amount' => 5000, 'provider' => 'wave', 'recipient' => ['phone' => '+221770000001']]);} catch (ApiException $e) { if ($e->getErrorCode() === 'insufficient_funds') { // afficher « solde insuffisant » } else { throw $e; }} catch (ConnectionException $e) { // résultat inconnu : réessayer plus tard avec la MÊME idempotency_key}| Exception | Quand |
|---|---|
Xaalis\Exception\XaalisException |
classe de base des trois suivantes |
ApiException |
l’API a répondu par une erreur : getStatus(), getErrorCode() (stable, voir Erreurs), getMessage(), getDetails() |
ConnectionException |
panne réseau ou délai dépassé après les nouvelles tentatives |
SignatureException |
un webhook n’a pas passé la vérification — répondez 400 |
Webhooks
Section intitulée « Webhooks »$raw = file_get_contents('php://input'); // les octets exacts, jamais un tableau ré-encodétry { $event = Xaalis\Webhook::constructEvent($raw, $_SERVER['HTTP_XAALIS_SIGNATURE'] ?? null, getenv('XAALIS_WEBHOOK_SECRET'));} catch (Xaalis\Exception\SignatureException $e) { http_response_code(400); exit;}
if ($event['livemode'] !== $expectLive || alreadyProcessed($event['id'])) { // test et production partagent une URL ; au moins une fois http_response_code(200); exit;}if ($event['type'] === 'payment.succeeded') { $payment = $event['data']['object']; // comparer $payment['amount'] et $payment['metadata'] à votre commande, puis la traiter une seule fois}http_response_code(200);constructEvent rejette un en-tête absent ou mal formé, un horodatage éloigné de plus de 300 s de l’heure actuelle
(rejeux), et une signature qui ne correspond à aucun v1 (comparaison avec hash_equals).
integrations/sdk-php/tests/docker-test.sh exécute php -l et la suite de tests dans php:8.3-cli. Si ADMIN_TOKEN
est défini dans le .env à la racine du dépôt, il exécute aussi un parcours réel contre l’API locale : création d’un
commerçant, d’un paiement de 15 000 (frais de 225), simulation de la réussite, vérification que le solde vaut 14 775,
et vérification qu’un versement de 99 000 000 échoue avec insufficient_funds.
Pour les boutiques WooCommerce, utilisez plutôt l’extension WooCommerce — elle ne nécessite pas Composer.