آموزش
ارسال ایمیل در Laravel
اتصال Laravel به سندک با SMTP یا API، و ارسال از صف.
آخرین ویرایش: ۲۱ آبان ۱۴۰۵
Laravel با چند خط تنظیم به سندک وصل میشود. پیش از شروع، دامنهتان را تأیید و یک کلید بسازید.
۱. تنظیم SMTP
در .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، کش تنظیمات را پاک کنید:
php artisan config:clear۲. اولین ایمیل
use Illuminate\Support\Facades\Mail;
Artisan::command('mail:test {to}', function (string $to) {
Mail::raw('سلام از سندک', fn ($message) => $message->to($to)->subject('اولین ایمیل'));
$this->info('در صف سندک قرار گرفت.');
});php artisan mail:test you@example.comدر پنل، «فعالیت» را باز کنید؛ ایمیل با خط زمانیاش باید آنجا باشد.
۳. ایمیل واقعی با Mailable
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:
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');
}
}'sendak' => ['key' => env('SENDAK_KEY')],$key را برای هر ایمیل منطقی ثابت بگیرید (مثلاً reset-{$request->id}) تا retry ایمیل تکراری نفرستد. جزئیات: کلید یکتایی درخواست.
۵. وبهوک
برای دانستن تحویل و برگشت، یک مسیر بسازید و امضا را بررسی کنید:
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:
->withMiddleware(function (Middleware $middleware) {
$middleware->validateCsrfTokens(except: ['webhooks/sendak']);
})اگر کار نکرد
| نشانه | علت محتمل |
|---|---|
| خطای احراز هویت | نام کاربری شناسهی کلید نیست یا راز کپی ناقص است |
| ایمیل رد میشود | MAIL_FROM_ADDRESS روی دامنهی تأییدشده نیست |
بعد از تغییر .env اثری نمیبینید | php artisan config:clear را نزدهاید |
| ایمیل میرود ولی نمیرسد | در «فعالیت» دلیل را ببینید؛ فهرست حذف را هم نگاه کنید |