لاراول محبوب‌ترین فریم‌ورک PHP در میان توسعه‌دهندگان وب است. ارسال پیامک‌های اعتبارسنجی ورود دو مرحله‌ای (OTP)، تایید ثبت سفارش، نوتیفیکیشن‌های سیستم و پیامک‌های تراکنشی هسته اصلی هر اپلیکیشن لاراولی مدرن به شمار می‌روند.

در این راهنما، نحوه پیاده‌سازی سرویس پیامک فری اس ام اس در لاراول ۱۰ و ۱۱ را به صورت ماژولار با معماری تمیز (Clean Architecture)، کانال نوتیفیکیشن اختصاصی (`Custom Notification Channel`) و صف‌های پس‌زمینه (`Queues`) آموزش می‌دهیم.

۱. تنظیمات محیطی (.env) و کانفیگ لاراول

ابتدا کلید وب‌سرویس و پیش‌شماره ارسالی را در فایل .env پروژه خود قرار دهید:

FREE_SMS_API_KEY=your_secret_api_key_here
FREE_SMS_BASE_URL=https://api.free-sms.ir
FREE_SMS_SENDER=50004000

سپس در فایل config/services.php تنظیمات سرویس را اضافه کنید:

<?php
return [
    // سایر کانفیگ‌ها...
    'freesms' => [
        'api_key'  => env('FREE_SMS_API_KEY'),
        'base_url' => env('FREE_SMS_BASE_URL', 'https://api.free-sms.ir'),
        'sender'   => env('FREE_SMS_SENDER'),
    ],
];

۲. ایجاد کلاس سرویس ارسال پیامک (FreeSmsService)

یک کلاس سرویس در مسیر app/Services/FreeSmsService.php ایجاد کنید. ما از کلاینت استاندارد Illuminate\Support\Facades\Http با مکانیزم Retry خودکار استفاده می‌کنیم:

<?php

namespace App\Services;

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Exception;

class FreeSmsService
{
    protected string $apiKey;
    protected string $baseUrl;
    protected ?string $sender;

    public function __construct()
    {
        $this->apiKey  = config('services.freesms.api_key');
        $this->baseUrl = config('services.freesms.base_url');
        $this->sender  = config('services.freesms.sender');
    }

    /**
     * ارسال سریع کد اعتبارسنجی OTP با پترن خدماتی
     */
    public function sendOtpPattern(string $mobile, string $patternCode, array $variables): array
    {
        try {
            $response = Http::withHeaders([
                'Authorization' => 'Bearer ' . $this->apiKey,
                'Content-Type'  => 'application/json',
            ])->timeout(4)->retry(2, 100)->post("{$this->baseUrl}/api/v1/send/pattern", [
                'recipient'    => $mobile,
                'pattern_code' => $patternCode,
                'variables'    => $variables,
            ]);

            if ($response->successful()) {
                return $response->json();
            }

            Log::error('FreeSMS OTP Error: ' . $response->body());
            throw new Exception('خطا در ارسال پیامک: ' . $response->status());
        } catch (Exception $e) {
            Log::critical('FreeSMS Connection Exception: ' . $e->getMessage());
            throw $e;
        }
    }
}

۳. ساخت کانال نوتیفیکیشن اختصاصی لاراول (Custom SmsChannel)

یکی از زیبایی‌های لاراول، سیستم یکپارچه Notifications آن است. برای ایجاد کانال پیامک سفارشی، فایل app/Notifications/Channels/SmsChannel.php را ایجاد کنید:

<?php

namespace App\Notifications\Channels;

use Illuminate\Notifications\Notification;
use App\Services\FreeSmsService;

class SmsChannel
{
    protected FreeSmsService $smsService;

    public function __construct(FreeSmsService $smsService)
    {
        $this->smsService = $smsService;
    }

    public function send($notifiable, Notification $notification)
    {
        if (!method_exists($notification, 'toSms')) {
            return;
        }

        $messageData = $notification->toSms($notifiable);
        $mobile = $notifiable->routeNotificationFor('sms', $notification);

        if (!$mobile) {
            $mobile = $notifiable->mobile ?? $notifiable->phone;
        }

        return $this->smsService->sendOtpPattern(
            $mobile,
            $messageData['pattern_code'],
            $messageData['variables']
        );
    }
}

۴. ارسال نوتیفیکیشن از طریق صف (Laravel Queues)

اکنون هر کلاسی که از Notification ارث‌بری می‌کند می‌تواند با افزودن implements ShouldQueue پیامک را بدون بلاک کردن درخواست کاربر ارسال کند:

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
use App\Notifications\Channels\SmsChannel;

class OrderConfirmedNotification extends Notification implements ShouldQueue
{
    use Queueable;

    protected $order;

    public function __construct($order)
    {
        $this->order = $order;
    }

    public function via($notifiable): array
    {
        return [SmsChannel::class];
    }

    public function toSms($notifiable): array
    {
        return [
            'pattern_code' => 'order-confirm',
            'variables'    => [
                'name'     => $notifiable->name,
                'order_id' => (string) $this->order->id,
                'amount'   => number_format($this->order->total),
            ],
        ];
    }
}
نکته کارایی در محیط پروداکشن: با اجرای دستور php artisan queue:work --queue=high,default می‌توانید صف ارسال پیامک‌های OTP و سفارشات را در اولویت بالاتر قرار دهید تا زیر ۲ ثانیه به دست مشتریان برسند.

۵. دریافت گزارش دلیوری با وب‌هوک لاراول (Webhook Controller)

هنگامی که وضعیت تحویل پیامک از سمت مخابرات مشخص می‌شود، فری اس ام اس آن را به وب‌هوک شما مخابره می‌کند:

<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;

class SmsWebhookController extends Controller
{
    public function handleDelivery(Request $request)
    {
        $messageId = $request->input('message_id');
        $status    = $request->input('status'); // DELIVERED, FAILED, BLACKLIST
        $deliveredAt = $request->input('delivered_at');

        Log::info("SMS {$messageId} updated to {$status} at {$deliveredAt}");

        return response()->json(['status' => 'acknowledged']);
    }
}

جدول کدهای وضعیت و خطاهای وب‌سرویس

کدهای پاسخ REST API فری اس ام اس
کد HTTP معنای خطا راهکار رفع خطا
200 OK درخواست با موفقیت ثبت و ارسال شد شناسه پیامک (Message ID) ذخیره شود
401 Unauthorized کلید API نامعتبر است متغیر FREE_SMS_API_KEY در فایل .env بررسی شود
402 Payment Required اعتبار ریالی حساب کافی نیست پنل کاربری فری اس ام اس شارژ شود
422 Unprocessable شماره گیرنده یا متغیرهای پترن ناقص است فرمت شماره به شکل 0912xxxxxxx ارسال شود
429 Too Many Requests تعداد درخواست بیش از سقف مجاز (Rate Limit) استفاده از صف‌های لاراول و توزیع بار درخواست

سوالات متداول برنامه‌نویسان لاراول

آیا برای ارسال پیامک در لاراول حتماً باید پکیج اختصاصی نصب کنیم؟+

خیر، با استفاده از HTTP Client پیش‌فرض لاراول (بر پایه Guzzle) می‌توانید تنها در یک کلاس سرویس ساده و سبک، بدون نیاز به نصب هیچ‌گونه وابستگی خارجی پکیج، وب‌سرویس RESTful فری اس ام اس را فراخوانی کنید.

چگونه ارسال پیامک در لاراول را به صف (Queue) بسپاریم تا سرعت پاسخگویی کنترلرها افت نکند؟+

کافی است اینترفیس ShouldQueue را به کلاس Notification یا Job لاراول خود اضافه کنید تا عملیات ارسال پیامک در پس‌زمینه توسط ورکرها پردازش شده و پاسخ کاربر بلافاصله بازگردانده شود.

آیا کدهای تایید OTP ارسالی از لاراول به دست کاربران بلک‌لیست مخابراتی می‌رسد؟+

بله، در صورت استفاده از اندپوینت /api/v1/send/pattern و قالب‌های تایید هویت پترن، پیامک از خطوط خدماتی اشتراکی ارسال شده و ۱۰۰٪ به دست کاربران بلک‌لیست می‌رسد.

در صورت بروز خطای تایم‌اوت شبکه در لاراول چه رویکردی پیشنهاد می‌شود؟+

می‌توانید از متد retry(3, 100) در کلاینت Http لاراول استفاده کنید تا در صورت بروز خطای موقت شبکه، درخواست به طور خودکار تا ۳ بار با فاصله ۱۰۰ میلی‌ثانیه تکرار شود.

آیا وب‌هوک گزارش دلیوری پیامک نیاز به غیرفعال‌سازی میدل‌ور CSRF در لاراول دارد؟+

بله، روت وب‌هوک باید در فایل routes/api.php تعریف شود یا در میدل‌ور VerifyCsrfToken لاراول در بخش $except قرار گیرد تا درخواست‌های POST سرور پیامک بدون خطای ۴۱۹ اعتبارسنجی شوند.