Packages
SDK PHP
Installer et utiliser labyrinthe/connect-php : SMS, OTP, webhooks et erreurs en PHP 8.1+.
Client PHP officiel : PHP 8.1+, ext-json, ext-mbstring. Utilise Guzzle par défaut, ou tout client PSR-18.
Version 1.2.0. Code source, exemples et journal des modifications : dossier php sur GitHub.
Installation#
composer require labyrinthe/connect-phpConfiguration#
<?php
use Labyrinthe\Connect\Client;
// From the environment: LABYRINTHE_API_KEY (required), LABYRINTHE_BASE_URL (optional)
$client = Client::fromEnv();
// Or explicitly (named arguments)
$client = new Client(
apiKey: 'lsk_test_xxxxxxxxxxxxxxxx',
timeout: 30.0, // seconds, per attempt
maxRetries: 2, // 0 to 10, on network errors, 429 and 5xx
defaultSender: 'MABOUTIQUE',
);
$client->isTestMode(); // true with a lsk_test_ key| Option | Défaut | Rôle |
|---|---|---|
apiKey | — | Clé lsk_live_… ou lsk_test_…. |
baseUrl | https://api.labyrinthe-rdc.com/api/public/v1 | Un hôte seul reçoit /api/public/v1. |
timeout / connectTimeout | 30.0 / 10.0 | Secondes. |
maxRetries / maxRetryWait | 2 / 10.0 | Relances et attente Retry-After maximale acceptée. |
defaultCountryCode | 243 | Indicatif des numéros locaux. |
defaultSender | null | Sender ID quand sender est omis. |
httpClient, logger | — | Client PSR-18 et journal PSR-3. |
Les services sont des propriétés : $client->sms, $client->balance, $client->senderIds, $client->otp. Chaque méthode accepte aussi requestId: (en-tête X-Request-Id) ; $client->lastResponse() donne le statut, l'identifiant de requête et les limites de la dernière réponse.
Envoyer un SMS#
<?php
use Labyrinthe\Connect\Client;
$client = new Client('lsk_test_xxxxxxxxxxxxxxxx'); // or Client::fromEnv() (LABYRINTHE_API_KEY)
$message = $client->sms->send(
to: '243812345678',
body: 'Votre commande 1042 est prête.',
reference: 'CMD-1042',
idempotencyKey: 'order-1042-ready', // replaying within 24 h sends nothing again
);
echo $message->id, ' ', $message->status->value; // queuedPlusieurs destinataires (jusqu'à 1000, tout ou rien) :
<?php
use Labyrinthe\Connect\Client;
$client = new Client('lsk_test_xxxxxxxxxxxxxxxx'); // or Client::fromEnv() (LABYRINTHE_API_KEY)
$batch = $client->sms->sendMany(
recipients: ['243812345678', '0899999999'],
body: 'Rappel : reunion demain 9h.',
sender: 'MABOUTIQUE', // an approved Sender ID of the account
reference: 'RAPPEL-2026-11-06',
idempotencyKey: 'reminder-2026-11-06',
);
foreach ($batch as $message) {
echo $message->recipient, ' ', $message->status->value, PHP_EOL;
}Estimer le coût, consulter le solde, lister les messages :
<?php
use Labyrinthe\Connect\Client;
$client = new Client('lsk_test_xxxxxxxxxxxxxxxx'); // or Client::fromEnv() (LABYRINTHE_API_KEY)
// Body first, then the recipients (duplicates are merged as for a real send).
$estimate = $client->sms->estimate('Votre code est 482913', ['243812345678', '0812345678', '0899999999']);
echo $estimate->encoding?->value, ' ', $estimate->totalCredits; // gsm7 2
if (! $estimate->isAffordable()) {
// top up before sending
}<?php
use Labyrinthe\Connect\Client;
$client = new Client('lsk_test_xxxxxxxxxxxxxxxx'); // or Client::fromEnv() (LABYRINTHE_API_KEY)
$balance = $client->balance->get();
echo $balance->balance, ' credits available, ', $balance->held, ' held';<?php
use Labyrinthe\Connect\Client;
$client = new Client('lsk_test_xxxxxxxxxxxxxxxx'); // or Client::fromEnv() (LABYRINTHE_API_KEY)
$page = $client->sms->list(status: 'delivered', from: '2026-11-01', perPage: 50);
echo $page->total, ' messages';
// Walk every page lazily
foreach ($page->autoPaging() as $message) {
echo $message->id, ' ', $message->recipient, PHP_EOL;
}Vérification OTP#
<?php
use Labyrinthe\Connect\Client;
use Labyrinthe\Connect\Exception\OtpResendTooSoonException;
$client = new Client('lsk_test_xxxxxxxxxxxxxxxx'); // or Client::fromEnv() (LABYRINTHE_API_KEY)
try {
$otp = $client->otp->send('243812345678', locale: 'fr', idempotencyKey: 'login-'.session_id());
} catch (OtpResendTooSoonException $e) {
exit('Wait '.$e->getRetryAfter().' s before asking for a new code.');
}
$_SESSION['otp_id'] = $otp->otpId; // keep it server-side
$otp->codeLength; // 6
$otp->resendAfterSeconds; // 60, for a "Resend" countdown
$otp->testCode(); // '482913' with a lsk_test_ key, null in live<?php
use Labyrinthe\Connect\Client;
$client = new Client('lsk_test_xxxxxxxxxxxxxxxx'); // or Client::fromEnv() (LABYRINTHE_API_KEY)
$result = $client->otp->verify($_SESSION['otp_id'], $_POST['code']);
if ($result->verified) {
unset($_SESSION['otp_id']); // phone verified: log the user in
} elseif ($result->reason?->value === 'invalid_code') {
echo 'Wrong code, ', $result->attemptsRemaining, ' attempt(s) left.';
} else {
echo 'Code expired or blocked: ask for a new one.'; // expired, max_attempts_reached, already_used
}Recevoir les webhooks#
<?php
use Labyrinthe\Connect\Exception\InvalidWebhookPayloadException;
use Labyrinthe\Connect\Exception\SignatureVerificationException;
use Labyrinthe\Connect\Webhook\Event\BalanceLow;
use Labyrinthe\Connect\Webhook\Event\MessageStatusChanged;
use Labyrinthe\Connect\Webhook\Webhook;
try {
$event = Webhook::constructEvent(
file_get_contents('php://input'),
$_SERVER['HTTP_X_SIGNATURE'] ?? '',
getenv('LABYRINTHE_WEBHOOK_SECRET'),
); // checks the signature and the timestamp (5 min)
} catch (SignatureVerificationException|InvalidWebhookPayloadException) {
http_response_code(400);
exit;
}
// Deduplicate on $event->id()
match (true) {
$event instanceof MessageStatusChanged => updateOrder($event->reference, $event->status->value),
$event instanceof BalanceLow => alertTeam($event->balance, $event->threshold),
default => null, // unknown events: answer 2xx
};Erreurs et maintenance#
La maintenance (503 FEATURE_DISABLED et OTP_SERVICE_DISABLED) a sa propre exception : rien n'a été envoyé ni débité. Le package relance seul si Retry-After est court (10 s par défaut) ; sinon, mettez l'envoi en file d'attente. Voir Erreurs.
<?php
use Labyrinthe\Connect\Client;
use Labyrinthe\Connect\Exception\ApiException;
use Labyrinthe\Connect\Exception\FeatureDisabledException;
use Labyrinthe\Connect\Exception\InsufficientCreditsException;
use Labyrinthe\Connect\Exception\RateLimitException;
use Labyrinthe\Connect\Exception\RecipientNotSendableException;
$client = new Client('lsk_test_xxxxxxxxxxxxxxxx'); // or Client::fromEnv() (LABYRINTHE_API_KEY)
try {
$client->sms->send(to: '243812345678', body: 'Bonjour');
} catch (FeatureDisabledException $e) {
// 503 maintenance: nothing was sent, no credit used
$e->getFeature(); // 'api.sms_send'
$e->getBlockedBy(); // 'api' when the whole API is off
$e->getAdminMessage(); // platform message, or null
$e->getUntil(); // ?DateTimeImmutable
$e->getRetryAfter() !== null ? retryIn($e->getRetryAfter()) : queueForLater();
} catch (InsufficientCreditsException $e) {
notifyTopUp($e->getAvailable(), $e->getRequired());
} catch (RecipientNotSendableException $e) {
foreach ($e->getRejected() as $rejected) {
echo $rejected->recipient, ' ', $rejected->reason->value, PHP_EOL;
}
} catch (RateLimitException $e) {
retryIn($e->getRetryAfter() ?? 60);
} catch (ApiException $e) {
logger($e->getErrorCode(), $e->getStatusCode(), $e->getRequestId());
}Mode test#
Avec une clé lsk_test_…, $client->isTestMode() vaut true et $otp->testCode() (une méthode, pas une propriété) renvoie le code. Webhook::sign($rawBody, $secret) fabrique une signature valide pour tester votre endpoint.