Laravel to najpopularniejszy framework PHP w Polsce. Dowiedz się, jak zintegrować SMS API — z użyciem HttpClient, kolejek i eventów.
Laravel to najpopularniejszy framework PHP w Polsce. Jeśli budujesz aplikację w Laravel i chcesz wysyłać SMS-y, ten artykuł pokaże Ci, jak zrobić to dobrze — z użyciem HttpClient, kolejek i eventów.
Wymagania
- Laravel 10+ (PHP 8.1+)
- Konto SMSIK z kluczem API
Krok 1: Konfiguracja klucza API
Dodaj klucz do .env:
SMSIK_API_KEY=sk_live_TWOJ_KLUCZ
SMSIK_API_URL=https://smsik.pl/api/v1
I do config/services.php:
'smsik' => [
'api_key' => env('SMSIK_API_KEY'),
'api_url' => env('SMSIK_API_URL', 'https://smsik.pl/api/v1'),
],
Krok 2: Serwis SMS
Utwórz app/Services/SmsikService.php:
<?php
namespace App\Services;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class SmsikService
{
private string $apiKey;
private string $apiUrl;
public function __construct()
{
$this->apiKey = config('services.smsik.api_key');
$this->apiUrl = config('services.smsik.api_url');
}
/**
* Wysyła SMS. Zwraca array z odpowiedzią API lub null przy błędzie.
*/
public function send(string $toNumber, string $message, ?string $idempotencyKey = null): ?array
{
$payload = [
'to_number' => $toNumber,
'message' => $message,
];
if ($idempotencyKey) {
$payload['idempotency_key'] = $idempotencyKey;
}
try {
$response = Http::withHeaders([
'X-API-Key' => $this->apiKey,
'Content-Type' => 'application/json',
])
->timeout(30)
->retry(3, 1000, function ($exception) {
// Retry tylko przy timeoutach i błędach 5xx
return $exception instanceof \Illuminate\Http\Client\ConnectionException
|| ($exception instanceof \Illuminate\Http\Client\RequestException
&& $exception->response->status() >= 500);
})
->post($this->apiUrl . '/sms/send', $payload);
if ($response->failed()) {
Log::error('SMSIK error', [
'status' => $response->status(),
'body' => $response->body(),
]);
return null;
}
return $response->json();
} catch (\Exception $e) {
Log::error('SMSIK exception', ['error' => $e->getMessage()]);
return null;
}
}
}
Krok 3: Job w kolejce
Wysyłka SMS-a to operacja sieciowa (może trwać 1-30 sekund). Nie blokuj nią requestu HTTP — użyj kolejki.
Utwórz app/Jobs/SendSmsJob.php:
<?php
namespace App\Jobs;
use App\Services\SmsikService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class SendSmsJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 3;
public int $backoff = 60; // sekundy między próbami
public function __construct(
public string $toNumber,
public string $message,
public ?string $idempotencyKey = null,
) {}
public function handle(SmsikService $smsik): void
{
$result = $smsik->send($this->toNumber, $this->message, $this->idempotencyKey);
if ($result === null) {
throw new \Exception('SMSIK send failed');
}
}
}
Krok 4: Użycie w kontrolerze
W kontrolerze (np. po złożeniu zamówienia):
use App\Jobs\SendSmsJob;
public function store(OrderRequest $request)
{
$order = Order::create($request->validated());
// Wyślij SMS w tle (nie blokuje requestu)
SendSmsJob::dispatch(
$order->phone,
"Zamowienie #{$order->id} przyjete. Dziekujemy!",
"order-{$order->id}-confirmation"
);
return response()->json(['order_id' => $order->id]);
}
Krok 5: Event + Listener (dla zaawansowanych)
Jeśli chcesz oddzielić logikę biznesową od wysyłki, użyj eventów:
// app/Events/OrderCreated.php
class OrderCreated
{
public function __construct(public Order $order) {}
}
// app/Listeners/SendOrderConfirmationSms.php
class SendOrderConfirmationSms
{
public function handle(OrderCreated $event): void
{
SendSmsJob::dispatch(
$event->order->phone,
"Zamowienie #{$event->order->id} przyjete. Dziekujemy!",
"order-{$event->order->id}-confirmation"
);
}
}
// W kontrolerze:
event(new OrderCreated($order));
Krok 6: Obsługa błędów i monitoringu
W SmsikService mamy już logowanie błędów. Dodatkowo możesz:
// W AppServiceProvider::boot()
use Illuminate\Support\Facades\Event;
Event::listen(\Illuminate\Queue\Events\JobFailed::class, function ($event) {
if ($event->job->resolveName() === \App\Jobs\SendSmsJob::class) {
Log::critical('SMS job failed permanently', [
'exception' => $event->exception->getMessage(),
]);
}
});
Krok 7: Test
W tests/Feature/SendSmsTest.php:
public function test_order_sends_sms(): void
{
Http::fake([
'smsik.pl/api/v1/sms/send' => Http::response([
'id' => 123,
'status' => 'queued',
'to_number' => '+48512017029',
'price_grosze' => 1,
], 200),
]);
$order = Order::factory()->create(['phone' => '+48512017029']);
SendSmsJob::dispatchSync($order->phone, 'Test', 'test-key');
Http::assertSent(function ($request) {
return $request->url() === 'https://smsik.pl/api/v1/sms/send'
&& $request->hasHeader('X-API-Key')
&& $request['to_number'] === '+48512017029';
});
}
Podsumowanie
- ✅ Serwis
SmsikService— reużywalny klient HTTP - ✅ Job w kolejce — nie blokuje requestu
- ✅ Retry z backoffem — odporność na timeouty
- ✅ Idempotencja — ochrona przed duplikatami
- ✅ Eventy — czysta architektura
- ✅ Testy —
Http::fake()
Zarejestruj się bezpłatnie — 5 groszy na start.