Aller au contenu

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ée

Les 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.

  • Idempotence : chaque create envoie une Idempotency-Key — un UUID v4 aléatoire si vous ne passez pas idempotency_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, 429 et 5xx, avec un délai exponentiel et prise en compte de Retry-After — uniquement quand rejouer la requête est sans risque : GET, PATCH, ou une création accompagnée de sa clé. Un 4xx n’est jamais relancé.
  • Entiers uniquement : amount doit être un int PHP ; 150.5, 15000.0 et "15000" lèvent \InvalidArgumentException avant 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
$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.