رفتن به محتوای اصلی
سندک

آموزش

ارسال ایمیل در Laravel

اتصال Laravel به سندک با SMTP یا API، و ارسال از صف.

آخرین ویرایش: ۲۱ آبان ۱۴۰۵

Laravel با چند خط تنظیم به سندک وصل می‌شود. پیش از شروع، دامنه‌تان را تأیید و یک کلید بسازید.

۱. تنظیم SMTP

در .env:

.env
MAIL_MAILER=smtp
MAIL_HOST=smtp.sendak.ir
MAIL_PORT=587
MAIL_USERNAME=شناسه_کلید
MAIL_PASSWORD=sd_live_…
MAIL_SCHEME=smtp
MAIL_FROM_ADDRESS="hello@shop.ir"
MAIL_FROM_NAME="${APP_NAME}"
  • MAIL_USERNAME شناسه‌ی کلید است و MAIL_PASSWORD راز آن.
  • در Laravel 11 به بعد، MAIL_SCHEME=smtp روی پورت ۵۸۷ خودش STARTTLS را شروع می‌کند. در نسخه‌های قدیمی‌تر به‌جای آن MAIL_ENCRYPTION=tls بگذارید.
  • MAIL_FROM_ADDRESS باید روی دامنه‌ی تأییدشده باشد، وگرنه ارسال رد می‌شود.

بعد از تغییر .env، کش تنظیمات را پاک کنید:

shell
php artisan config:clear

۲. اولین ایمیل

routes/console.php
use Illuminate\Support\Facades\Mail;

Artisan::command('mail:test {to}', function (string $to) {
    Mail::raw('سلام از سندک', fn ($message) => $message->to($to)->subject('اولین ایمیل'));
    $this->info('در صف سندک قرار گرفت.');
});
shell
php artisan mail:test you@example.com

در پنل، «فعالیت» را باز کنید؛ ایمیل با خط زمانی‌اش باید آنجا باشد.

۳. ایمیل واقعی با Mailable

app/Mail/OrderShipped.php
namespace App\Mail;

use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class OrderShipped extends Mailable implements ShouldQueue
{
    use Queueable, SerializesModels;

    public function __construct(public Order $order) {}

    public function envelope(): Envelope
    {
        return new Envelope(subject: "سفارش {$this->order->number} ارسال شد");
    }

    public function content(): Content
    {
        return new Content(view: 'mail.order-shipped');
    }
}
در کنترلر
Mail::to($order->email)->send(new OrderShipped($order));

با ShouldQueue ارسال در صف Laravel انجام می‌شود و درخواست کاربر منتظر SMTP نمی‌ماند.

نکته: SMTP کلید یکتایی درخواست ندارد. اگر Job شما بعد از قطع اتصال دوباره اجرا شود، ممکن است ایمیل دو بار برود. برای ایمیل‌هایی که تکرارشان بد است (مثل کد تأیید) از API استفاده کنید.

۴. ارسال با API و Idempotency-Key

برای کنترل بیشتر، مستقیم با Http:

app/Services/Sendak.php
namespace App\Services;

use Illuminate\Support\Facades\Http;

class Sendak
{
    /** @param list<string> $to */
    public function send(string $key, array $to, string $subject, string $text, array $tags = []): array
    {
        return Http::withToken(config('services.sendak.key'))
            ->withHeaders(['Idempotency-Key' => $key])
            ->timeout(10)
            ->retry(3, 500, throw: false)
            ->post('https://api.sendak.ir/v1/emails', [
                'from' => config('mail.from.address'),
                'to' => $to,
                'subject' => $subject,
                'text' => $text,
                'tags' => $tags,
            ])
            ->throw()
            ->json('data');
    }
}
config/services.php
'sendak' => ['key' => env('SENDAK_KEY')],

$key را برای هر ایمیل منطقی ثابت بگیرید (مثلاً reset-{$request->id}) تا retry ایمیل تکراری نفرستد. جزئیات: کلید یکتایی درخواست.

۵. وب‌هوک

برای دانستن تحویل و برگشت، یک مسیر بسازید و امضا را بررسی کنید:

routes/web.php
Route::post('/webhooks/sendak', function (Request $request) {
    parse_str(str_replace(',', '&', (string) $request->header('X-Sendak-Signature')), $parts);
    $t = (int) ($parts['t'] ?? 0);
    $expected = hash_hmac('sha256', $t.'.'.$request->getContent(), config('services.sendak.webhook_secret'));

    abort_unless($t > 0 && abs(time() - $t) <= 300 && hash_equals($expected, (string) ($parts['v1'] ?? '')), 401);

    // کار سنگین را در صف انجام بدهید و سریع 200 برگردانید.
    return response()->noContent();
});

مسیر وب‌هوک باید از CSRF معاف باشد؛ امنیت آن را امضا تأمین می‌کند. در bootstrap/app.php:

bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    $middleware->validateCsrfTokens(except: ['webhooks/sendak']);
})

اگر کار نکرد

نشانهعلت محتمل
خطای احراز هویتنام کاربری شناسه‌ی کلید نیست یا راز کپی ناقص است
ایمیل رد می‌شودMAIL_FROM_ADDRESS روی دامنه‌ی تأییدشده نیست
بعد از تغییر .env اثری نمی‌بینیدphp artisan config:clear را نزده‌اید
ایمیل می‌رود ولی نمی‌رسددر «فعالیت» دلیل را ببینید؛ فهرست حذف را هم نگاه کنید

کلید ندارید؟

با پلن رایگان، ۱٬۰۰۰ ایمیل در ماه می‌فرستید.