Intégrer l’API Coussema en PHP
Ce guide est fait pour un backend PHP ou Laravel qui doit envoyer des OTP, notifications transactionnelles ou alertes métier via l’API REST Coussema.
Pré-requis côté serveur
La clé API doit rester sur le serveur PHP. Ne l’exposez jamais dans une page HTML, une application mobile ou un JavaScript public.
GuzzleHttp\Client
composer require guzzlehttp/guzzle<?php
require __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
$apiKey = getenv('COUSSEMA_API_KEY');
if (!$apiKey) {
throw new RuntimeException('COUSSEMA_API_KEY manquant.');
}
$client = new Client([
'base_uri' => getenv('COUSSEMA_BASE_URL') ?: 'https://api.coussema.com',
'timeout' => 15,
]);
try {
$response = $client->post('/v1/sms/send', [
'headers' => [
'Authorization' => 'Bearer ' . $apiKey,
'Content-Type' => 'application/json',
'Idempotency-Key' => bin2hex(random_bytes(16)),
'X-Request-Id' => 'php-send-' . date('YmdHis'),
],
'json' => [
'to' => '+243980395067',
'message' => 'Votre code : 4829',
'senderName' => 'MonApp',
'clientReference' => 'otp-login-4829',
],
]);
$payload = json_decode((string) $response->getBody(), true, 512, JSON_THROW_ON_ERROR);
if (($payload['queued'] ?? false) !== true || ($payload['status'] ?? null) !== 'accepted') {
throw new RuntimeException('SMS non accepté par Coussema.');
}
echo 'SMS mis en file. Quota restant: ' . ($payload['remainingQuota'] ?? 'n/a');
} catch (RequestException $e) {
$body = $e->getResponse() ? (string) $e->getResponse()->getBody() : '';
throw new RuntimeException('Erreur API Coussema: ' . $body, 0, $e);
}COUSSEMA_API_KEY
Clé API générée depuis le dashboard Coussema. Elle reste côté serveur.
ck_live_xxxCOUSSEMA_BASE_URL
Hôte public de production. Les exemples appellent `/v1/sms/send`.
https://api.coussema.comCOUSSEMA_WEBHOOK_SECRET
Secret reçu lors de la création d’un webhook, à stocker dans `.env`.
csm_whsec_xxxComprendre la réponse API
`POST /v1/sms/send` répond avec un succès `2xx` quand Coussema a validé, persisté et mis le SMS en file. La livraison finale arrive ensuite via webhook ou rapport DLR.
PHP cURL natif
Variante sans dépendance Composer, utile pour un hébergement PHP simple.
<?php
$payload = [
'to' => '+243980395067',
'message' => 'Votre code : 4829',
'senderName' => 'MonApp',
'clientReference' => 'otp-login-4829',
];
$ch = curl_init('https://api.coussema.com/v1/sms/send');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('COUSSEMA_API_KEY'),
'Content-Type: application/json',
'Idempotency-Key: ' . bin2hex(random_bytes(16)),
],
CURLOPT_POSTFIELDS => json_encode($payload, JSON_THROW_ON_ERROR),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($body === false) {
throw new RuntimeException('Erreur réseau: ' . curl_error($ch));
}
curl_close($ch);
if ($status < 200 || $status >= 300) {
throw new RuntimeException('Erreur API Coussema HTTP ' . $status . ': ' . $body);
}
$result = json_decode($body, true, 512, JSON_THROW_ON_ERROR);Laravel controller
Exemple de contrôleur pour déclencher un OTP depuis une route backend Laravel.
<?php
namespace App\Http\Controllers;
use GuzzleHttp\Client;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
class CoussemaSmsController
{
public function sendOtp(Request $request): JsonResponse
{
$request->validate([
'phone' => ['required', 'string'],
]);
$code = random_int(1000, 9999);
$client = new Client([
'base_uri' => config('services.coussema.base_url', 'https://api.coussema.com'),
'timeout' => 15,
]);
$response = $client->post('/v1/sms/send', [
'headers' => [
'Authorization' => 'Bearer ' . config('services.coussema.key'),
'Content-Type' => 'application/json',
'Idempotency-Key' => (string) Str::uuid(),
'X-Request-Id' => 'laravel-otp-' . Str::uuid(),
],
'json' => [
'to' => $request->input('phone'),
'message' => 'Votre code : ' . $code,
'senderName' => 'MonApp',
'clientReference' => 'otp-' . now()->timestamp,
],
]);
$payload = json_decode((string) $response->getBody(), true, 512, JSON_THROW_ON_ERROR);
return response()->json([
'accepted' => ($payload['queued'] ?? false) === true,
'status' => $payload['status'] ?? null,
'request_id' => $response->getHeaderLine('x-request-id'),
], $response->getStatusCode());
}
}Configuration Laravel
Ajoutez cette entrée dans `config/services.php`, puis utilisez `config('services.coussema.key')`.
'coussema' => [
'key' => env('COUSSEMA_API_KEY'),
'base_url' => env('COUSSEMA_BASE_URL', 'https://api.coussema.com'),
'webhook_secret' => env('COUSSEMA_WEBHOOK_SECRET'),
],Vérifier un webhook en PHP
Les webhooks permettent de confirmer `message.delivered` et `message.failed` sans interroger l’API en boucle.
<?php
$rawBody = file_get_contents('php://input') ?: '';
$timestamp = $_SERVER['HTTP_X_COUSSEMA_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_COUSSEMA_SIGNATURE'] ?? '';
$secret = getenv('COUSSEMA_WEBHOOK_SECRET');
if (!$timestamp || !$signature || !$secret) {
http_response_code(400);
exit('Signature manquante.');
}
$signedPayload = $timestamp . '.' . $rawBody;
$expected = 'sha256=' . hash_hmac('sha256', $signedPayload, $secret);
if (!hash_equals($expected, $signature)) {
http_response_code(401);
exit('Signature invalide.');
}
$event = json_decode($rawBody, true, 512, JSON_THROW_ON_ERROR);
// Exemple: message.delivered ou message.failed
// Mettez à jour votre commande, paiement, ticket ou session OTP ici.
http_response_code(204);Points à ne pas rater
Ces règles évitent les erreurs d’intégration les plus fréquentes chez les backends PHP.