# Enviar emails con Laravel

> Envía el correo de Laravel a través de Emailit con el transporte de correo y la facade de emailit/emailit-laravel, o con SMTP sin más, y verifica los webhooks de Emailit.

En esta guía se explican dos formas de enviar el correo de Laravel a través de Emailit: el paquete `emailit/emailit-laravel`, que añade un transporte de correo `emailit` y una facade `Emailit`, y SMTP sin más con el mailer integrado de Laravel. Al final se incluye una ruta de webhook que verifica las firmas.

## Requisitos previos

- PHP 8.1 o posterior y Laravel 10, 11 o 12.
- Un [dominio de envío verificado](/es/docs/domains/add-a-domain/), por ejemplo `acme.com`.
- Una [clave de API](/es/docs/developers/api-keys/). Basta con una clave Sending Only limitada a tu dominio.
- Hasta que tu espacio de trabajo tenga [acceso de producción](/es/docs/workspaces/production-access/), solo puedes enviar a las direcciones de email de las cuentas de los miembros del espacio de trabajo.

## Opción 1: el paquete de Laravel de Emailit

El paquete envía tus Mailables, Markdown mailables, notificaciones y correo en cola actuales a través de la API de Emailit, sin cambiar tu código de correo.

### Instalar el paquete

```bash
composer require emailit/emailit-laravel
```

El service provider se detecta automáticamente.

### Configurar el transporte

Añade tu clave y haz que Emailit sea el mailer por defecto en `.env`:

```bash title=".env"
EMAILIT_API_KEY=secret_••••••••••••••••••••••••••••••••
MAIL_MAILER=emailit
MAIL_FROM_ADDRESS=hello@acme.com
MAIL_FROM_NAME="Acme"
```

Registra el mailer en `config/mail.php`:

```php title="config/mail.php"
'mailers' => [
    // ...

    'emailit' => [
        'transport' => 'emailit',
    ],
],
```

`MAIL_FROM_ADDRESS` debe pertenecer a un dominio de envío verificado. Para cambiar la URL base de la API, publica el archivo de configuración con `php artisan vendor:publish --tag=emailit-config`; para un uso normal no hace falta.

### Enviar un Mailable

Crea un Mailable como de costumbre:

```bash
php artisan make:mail WelcomeEmail
```

```php title="app/Mail/WelcomeEmail.php"
namespace App\Mail;

use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class WelcomeEmail extends Mailable
{
    use Queueable, SerializesModels;

    public function __construct(public User $user) {}

    public function envelope(): Envelope
    {
        return new Envelope(subject: 'Welcome to Acme');
    }

    public function content(): Content
    {
        return new Content(view: 'emails.welcome');
    }
}
```

Envíalo, o ponlo en cola para que la petición no espere a la API:

```php
use App\Mail\WelcomeEmail;
use Illuminate\Support\Facades\Mail;

Mail::to($user)->send(new WelcomeEmail($user));

Mail::to($user)->queue(new WelcomeEmail($user));
```

### Usar la facade para las funciones de la API

La facade `Emailit` expone todo el [SDK de PHP](/es/docs/frameworks/php/) para las funciones que el mailer de Laravel no contempla, como las plantillas guardadas y los envíos programados:

```php
use Emailit\Laravel\Facades\Emailit;

$email = Emailit::emails()->send([
    'from'         => 'Acme <hello@acme.com>',
    'to'           => $user->email,
    'template'     => 'welcome',
    'variables'    => ['first_name' => $user->first_name],
    'scheduled_at' => 'tomorrow at 9am',
]);

$email->id; // em_…
```

La facade también cubre los dominios, los contactos, las listas de contactos, las direcciones bloqueadas, los webhooks y los demás recursos. Si prefieres la inyección de dependencias, declara el tipo `Emailit\EmailitClient` en un controlador o un job y Laravel lo resuelve con la clave que has configurado.

Captura las excepciones tipadas para gestionar los fallos:

```php
use Emailit\Exceptions\ApiErrorException;
use Emailit\Exceptions\RateLimitException;

try {
    Emailit::emails()->send($payload);
} catch (RateLimitException $e) {
    // 429: release the job back to the queue and try again later
} catch (ApiErrorException $e) {
    report($e); // $e->getHttpStatus() has the status code
}
```

## Opción 2: SMTP sin más

Si prefieres no añadir un paquete, apunta el mailer SMTP de Laravel al relay de Emailit:

```bash title=".env"
MAIL_MAILER=smtp
MAIL_HOST=smtp.emailit.com
MAIL_PORT=587
MAIL_USERNAME=emailit
MAIL_PASSWORD=secret_••••••••••••••••••••••••••••••••
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=hello@acme.com
MAIL_FROM_NAME="Acme"
```

En el puerto 587, el mailer de Laravel actualiza la conexión con STARTTLS. Los archivos `config/mail.php` antiguos leen `MAIL_ENCRYPTION` y los nuevos lo ignoran, así que puedes mantenerlo sin problemas. Para TLS implícito, usa el puerto `465`; si tu proveedor de hosting bloquea el 587, usa `2525` o `2587`. Los Mailables, las notificaciones y las colas funcionan exactamente igual que con el paquete. Consulta [Configuración SMTP](/es/docs/smtp/settings/).

Por SMTP no puedes usar plantillas guardadas ni `scheduled_at`; para eso, usa la facade o la API.

## Recibir webhooks

[Crea un webhook](/es/docs/webhooks/set-up/) que apunte a `https://your-app.com/webhooks/emailit` y después guarda su secreto de firma:

```bash title=".env"
EMAILIT_WEBHOOK_SECRET=whsec_••••••••
```

```php title="config/services.php"
'emailit' => [
    'webhook_secret' => env('EMAILIT_WEBHOOK_SECRET'),
],
```

Añade un controlador que compruebe la firma con el cuerpo en bruto:

```php title="app/Http/Controllers/EmailitWebhookController.php"
namespace App\Http\Controllers;

use Illuminate\Http\Request;

class EmailitWebhookController extends Controller
{
    public function __invoke(Request $request)
    {
        $payload = $request->getContent();
        $signature = (string) $request->header('X-Emailit-Signature');
        $timestamp = (string) $request->header('X-Emailit-Timestamp');

        $expected = hash_hmac('sha256', $timestamp.'.'.$payload, config('services.emailit.webhook_secret'));

        if (abs(time() - (int) $timestamp) > 300 || ! hash_equals($expected, $signature)) {
            abort(401, 'Invalid signature');
        }

        // The body is a JSON array of up to 100 events.
        foreach (json_decode($payload, true) as $event) {
            match ($event['type']) {
                'email.bounced', 'email.complained' => $this->stopEmailing($event['data']['object']['to']),
                default => null,
            };
        }

        return response()->noContent();
    }

    private function stopEmailing(string $address): void
    {
        // Mark the address as undeliverable in your database.
    }
}
```

Registra la ruta y exclúyela de la protección CSRF, porque Emailit no puede enviar un token CSRF:

```php title="routes/web.php"
use App\Http\Controllers\EmailitWebhookController;

Route::post('/webhooks/emailit', EmailitWebhookController::class);
```

```php title="bootstrap/app.php (Laravel 11 and 12)"
->withMiddleware(function (Middleware $middleware) {
    $middleware->validateCsrfTokens(except: ['webhooks/emailit']);
})
```

En Laravel 10, añade en su lugar `'webhooks/emailit'` al array `$except` de `app/Http/Middleware/VerifyCsrfToken.php`. Devuelve un `2xx` en menos de 30 segundos y pasa el trabajo lento a una cola. Consulta [Firma de las peticiones](/es/docs/webhooks/request-signature/).

## Consejos para producción

- **Pon el correo en cola.** Usa `queue()` o `ShouldQueue` para que las peticiones web no esperen al email, y limita el ritmo de los jobs masivos (por ejemplo, con `Redis::throttle`) para no superar tus [límites de envío](/es/docs/limits/). Por defecto, los espacios de trabajo nuevos pueden enviar 2 emails por segundo.
- **Usa la caché de configuración de forma segura.** Después de `php artisan config:cache`, `env()` solo funciona dentro de los archivos de configuración. Lee la clave a través de la configuración, como hace el paquete.
- **Usa una clave dedicada.** Da a cada aplicación y a cada entorno su propia clave Sending Only para poder rotar una sin tocar las demás. Consulta [Claves de API](/es/docs/developers/api-keys/).
- **Elimina los webhooks duplicados.** Guarda cada `event_id` que procesas y omite las repeticiones, porque las entregas fallidas se reintentan.

## Próximos pasos

  - [Guía del SDK de PHP](/es/docs/frameworks/php/): El cliente que hay detrás de la facade.
  - [Plantillas](/es/docs/templates/): Diseña emails en Emailit y envíalos por su alias.
  - [Tipos de eventos de webhook](/es/docs/webhooks/event-types/): Todos los eventos y su payload.
  - [Solución de problemas de SMTP](/es/docs/smtp/troubleshooting/): Corrige los errores de autenticación y de conexión.

---
Fuente: https://emailit.com/es/docs/frameworks/laravel/
