Mailgun emails not sending from Laravel 8 Valet app

Viewed 638

Here's my relevant .env:

MAIL_MAILER=mailgun
MAIL_HOST=smtp.mailgun.org
MAIL_PORT=587
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
MAILGUN_DOMAIN=https://api.mailgun.net/v3/mail.example.com
MAILGUN_SECRET=fb...a1

Note: I use example.com as an example above, but I've put my actual domain name there. I do not get any errors from the Laravel app, and I do not see anything in the logs on the Mailgun dashboard. My domain is verified. fb...a1 is also the redacted API code, I of course use my full API code I get from the mailgun dashboard.

config/mail.php:

<?php

return [
    'default' => env('MAIL_MAILER', 'mailgun'),

    'mailers' => [
        'mailgun' => [
            'transport' => 'mailgun',
        ],
    ],
];

config/services.php:

'mailgun' => [
    'domain' => env('MAILGUN_DOMAIN'),
    'secret' => env('MAILGUN_SECRET'),
    'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
],

In my controller I have:

$email = $validated['email']; // I've verified this is my actual email
Mail::to($email)->send(new OrderCreated());

app/Mail/OrderCreated.php:

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class OrderCreated extends Mailable
{
    use Queueable, SerializesModels;

    public function __construct()
    {
        //
    }

    public function build()
    {
        return $this
            ->from('no-reply@example.com')
            ->markdown('emails.order-created');
    }
}

And finally, resources/views/emails/order-created.blade.php:

@component('mail::message')
# Order Confirmation

This email is  test.
@endcomponent

I'm using Laravel 8.14.0 and Valet 2.13.0, so I'm testing it locally with an https://my-app.test domain. The app is using InertiaJS, in case that makes any difference. The controller code runs without error, but I see no logs on my mailgun dashboard and the email never arrives in my inbox. I have no idea what's wrong or how to debug this.

UPDATE:

I've noticed if I set MAILGUN_DOMAIN and MAILGUN_SECRET to null, I get the same behaviour as above. If I set MAILGUN_DOMAIN to a nonsense value like abcd I get the following error:

GuzzleHttp\Exception\ClientException
Client error: `POST https://api.mailgun.net/v3/abcd/messages.mime` resulted in a `401 UNAUTHORIZED` response: Forbidden

And if I set MAILGUN_SECRET to abcd it works as originally described (no error, but also no email).

1 Answers

These days I've worked with mailgun. It was on Laravel 7, and it worked perfectly. I dont think that it will cause a problem for v8. Actually I've sent emails from controller, but you can implement the same for you as you want. So I'll share my experience anyway.

.env

#MAIL_DRIVER=mailgun
MAIL_MAILER=mailgun
MAIL_HOST="smtp.mailgun.org"
MAIL_PORT=587
MAIL_USERNAME="postmaster@sandbox********************************.mailgun.org"
MAIL_PASSWORD="123456"
MAIL_ENCRYPTION=tls
MAILGUN_DOMAIN="sandbox********************************.mailgun.org"
MAILGUN_SECRET="key-********************************"
MAIL_FROM_NAME="ProjectName"
MAIL_FROM_ADDRESS="no-reply@yourfuturesite.com"
MAIL_ENV=test
MAIL_TEST="recipient@yourfuturesite.com"
#MAIL_LOG_CHANNEL
#MAILGUN_ENDPOINT="api.eu.mailgun.net"

config/mail.php

<?php

return [
    'default' => env('MAIL_MAILER'),
    'mailers' => [
        'smtp' => [
            'transport' => 'smtp',
            'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
            'port' => env('MAIL_PORT', 587),
            'encryption' => env('MAIL_ENCRYPTION', 'tls'),
            'username' => env('MAIL_USERNAME'),
            'password' => env('MAIL_PASSWORD'),
            'timeout' => null,
            'auth_mode' => null,
        ],
        'ses' => [
            'transport' => 'ses',
        ],
        'mailgun' => [
            'transport' => 'mailgun',
            'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
            'port' => env('MAIL_PORT', 587),
            'encryption' => env('MAIL_ENCRYPTION', 'tls'),
            'username' => env('MAIL_USERNAME'),
            'password' => env('MAIL_PASSWORD'),
            'timeout' => null,
            'auth_mode' => null,
        ],
        'postmark' => [
            'transport' => 'postmark',
        ],
        'sendmail' => [
            'transport' => 'sendmail',
            'path' => '/usr/sbin/sendmail -bs',
        ],
        'log' => [
            'transport' => 'log',
            'channel' => env('MAIL_LOG_CHANNEL'),
        ],
        'array' => [
            'transport' => 'array',
        ],
    ],
    'from' => [
        'address' => env('MAIL_FROM_ADDRESS'),
        'name' => env('MAIL_FROM_NAME'),
    ],
    'markdown' => [
        'theme' => 'default',
        'paths' => [
            resource_path('views/vendor/mail'),
        ],
    ],
    // CUSTOM CONFIGS
    'mail_env' => env('MAIL_ENV'), // 'local' for testing via mailgun, 'production' for all mails
    'mail_test' => env('MAIL_TEST'), // test email for 'local' testing
    // ADDITIONAL UNNECESSARY CONFIGS
    // 'sendmail' => '/usr/sbin/sendmail -bs',
    // 'sendmail' => '/usr/sbin/sendmail -t-i',
    // 'pretend' => false,
    // 'log_channel' => env('MAIL_LOG_CHANNEL'),
    // 'pretend' => false,
];

config/services.php (the same as you have)

Controller

try {
    $name = array_key_exists('first_name', $email_data) ? $email_data['first_name'] : $email_data['name'];

    Mail::send('emails.confirm-registration', [
        'role' => $email_data['role'],
        'name' => $name,
        'email' => $email_data['email'],
        'confirm_registration' => route('front.auth.confirm_registration', ['registration_token' => $email_data['registration_token']]),
    ], function ($message) use ($email_data, $name) {
        $to = (config('mail.mail_env') == 'prod' || config('mail.mail_env') == 'production') ? $email_data['email'] : config('mail.mail_test');
        $message
            ->subject(config('app.name') . ": Email Confirmation")
            ->from(config('mail.from.address'), config('mail.from.name'))
            ->to($to, $name);
    });

    return true;
}
catch(\Exception $e) {
    // TODO: report all the "$e->message"s like this
    return false;
}

resources/views/emails/confirm-registration.php

<a href="{{ route('front.main') }}" target="_blank">
    <img label="logo" alt="{{ config('app.name') }}"
         src="{{ $message->embed(public_path('images/logo.svg')) }}" width="128" height="96">
</a>
<p>{{ $name }}</p>
<p>{{ $confirm_registration }}</p>
<p>© {{ date("Y") == 2020 ? '2020' : '2020-' . date("Y") }} {{ config('app.name') }}</p>

This is just my example, so there you may need to replace as you want. Just pay attention to "config/mail.php" file. I think there you need to set some additional props. (Also don't forget to reload your cache after these last config changes: "php artisan config:cache")

Related