SMS mit PHP versenden: cURL und Composer
SMS aus PHP in wenigen Zeilen: reines cURL oder die Bibliothek topsms/topsms-php, sichere Wiederholungen per Idempotency-Key und signierte Zustellberichte.
Senden Sie mit cURL einen POST an die TopSMS-API, eine Anfrage pro SMS, oder installieren Sie topsms/topsms-php per Composer und rufen Sie $client->send() auf. Ergänzen Sie einen Idempotency-Key für sichere Wiederholungen und prüfen Sie bei Webhooks den Header X-TopSMS-Signature.
Was Sie brauchen
- Ein kostenloses TopSMS-Konto; nach der Bestätigung Ihrer Mobilnummer haben Sie 0,50 € Startguthaben zum Testen.
- Einen API-Schlüssel (Client-ID und Secret) aus dem Dashboard unter API. Legen Sie ihn in Umgebungsvariablen ab, nicht im Code.
- PHP 7.4 oder neuer mit
ext-curlundext-json.
Rufnummern im internationalen Format (+49…, +43…). Als Absender können Sie Ihren Firmennamen mit bis zu 11 Zeichen verwenden; deutsche und österreichische Netze stellen ihn ohne Registrierung zu.
Variante 1: reines cURL, ohne Abhängigkeiten
<?php
$ch = curl_init('https://www.topsms.cz/api/sms/send');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('TOPSMS_CLIENT_ID') . ':' . getenv('TOPSMS_SECRET'),
'Content-Type: application/json',
'Idempotency-Key: order-1024-shipped',
],
CURLOPT_POSTFIELDS => json_encode([
'to' => '+4915112345678',
'text' => 'MeinShop: Ihre Bestellung 1024 wurde versandt.',
'from' => 'MeinShop',
]),
]);
$body = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$result = json_decode($body, true);
if ($code !== 200) {
error_log("SMS fehlgeschlagen ($code): " . ($result['error'] ?? $body));
}Die Antwort enthält id (für den Status), smsCount (berechnete Segmente) und price. Der Header Idempotency-Key macht Wiederholungen sicher: Läuft Ihr Skript in einen Timeout und sendet dieselbe Anfrage mit demselben Schlüssel erneut, wird die SMS nicht doppelt verschickt. Verwenden Sie einen Schlüssel pro fachlichem Ereignis, etwa Bestellnummer plus Ereignis.
Variante 2: die Composer-Bibliothek
composer require topsms/topsms-php<?php
require 'vendor/autoload.php';
use TopSms\TopSmsClient;
use TopSms\TopSmsException;
$client = new TopSmsClient(getenv('TOPSMS_CLIENT_ID'), getenv('TOPSMS_SECRET'));
try {
$result = $client->send('+4915112345678', 'MeinShop: Ihre Bestellung 1024 wurde versandt.', 'MeinShop');
echo "Gesendet, ID {$result['id']}\n";
$status = $client->status($result['id']); // sent | delivered | failed | expired
$credit = $client->credit();
} catch (TopSmsException $e) {
error_log("SMS fehlgeschlagen ({$e->getHttpCode()}): {$e->getMessage()}");
}Die Bibliothek hat keine weiteren Abhängigkeiten und wirft TopSmsException mit dem HTTP-Code: 400 bei ungültiger Nummer oder fehlendem Feld, 401 bei falschen Zugangsdaten, 402 bei zu wenig Guthaben, 403 bei inaktivem Konto oder einer IP außerhalb der Whitelist, 429 beim Rate-Limit (standardmäßig 60 Anfragen pro Minute und Schlüssel).
Zustellberichte per Webhook
Den Status abzufragen funktioniert, ein Webhook ist aber sauberer. Hinterlegen Sie beim API-Schlüssel im Dashboard eine Webhook-URL; jede Statusänderung kommt als JSON-POST mit dem Header X-TopSMS-Signature, einem HMAC-SHA256 über den Rohinhalt. Prüfen Sie ihn gegen den Rohinhalt, nicht gegen das geparste JSON:
<?php
require 'vendor/autoload.php';
use TopSms\TopSmsClient;
$raw = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_TOPSMS_SIGNATURE'] ?? '';
if (!TopSmsClient::verifyWebhookSignature($raw, $signature, getenv('TOPSMS_WEBHOOK_SECRET'))) {
http_response_code(401);
exit;
}
$event = json_decode($raw, true);
// $event['status']: delivered | failed | expired
http_response_code(200);Antworten Sie innerhalb von 5 Sekunden mit HTTP 2xx. Fehlgeschlagene Zustellungen wiederholen wir dreimal (sofort, nach 30 Sekunden, nach 2 Minuten).
Testen ohne Versand
Legen Sie im Dashboard einen Testschlüssel an. Er liefert dieselben Antworten wie ein Produktivschlüssel, zusätzlich "test": true, sendet und berechnet nichts und simuliert Statuswechsel und Webhook nach 2 bis 4 Sekunden. Nummern, die auf 0000 enden, simulieren einen Fehler, 0001 den Ablauf. So ist die ganze Integration samt Webhooks fertig, bevor Sie Guthaben aufladen.
Typische Stolperfallen
- Zeichen: Ein Emoji oder typografische Anführungszeichen schalten die Nachricht auf Unicode um, das Limit sinkt von 160 auf 70 Zeichen. Umlaute und ß sind unkritisch. Siehe SMS-Länge und Zeichen.
- Rufnummernformat: Normalisieren Sie Eingaben wie
0151 1234567vor dem Versand zu+49151…. - Viele Empfänger: Keine Schleife über Tausende Einzelanfragen;
/api/sms/send-bulknimmt bis zu 500 Empfänger pro Anfrage. Siehe Massen-SMS versenden. - WordPress: Für WooCommerce gibt es ein fertiges Plugin, ganz ohne Code (Integrationen).
Was kostet der SMS-Versand aus PHP?
Die API kostet nichts extra: Eine SMS nach Deutschland oder Österreich kostet 0,084 € pro Segment, ohne Grundgebühr, aus Prepaid-Guthaben ohne Verfall. Die vollständige Tabelle finden Sie auf der Preisseite, die komplette API-Referenz in der Dokumentation.