Frameworki

Integracja SMS API w Laravel — krok po kroku

Tomasz Lewandowski — Lead Developer, SMSIK 10 min czytania

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.

Wypróbuj SMSIK bezpłatnie

Rejestracja zajmuje 2 minuty. Otrzymasz 5 groszy na start.

Utwórz darmowe konto
ŚRODOWISKO DEWELOPERSKIE - wersja testowa (nieprodukcyjna)