Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Gabotronix's avatar

Programatically check if Mailgun mail was sent correctly with PHP SDK

Hi everybody, I 'm tinkering with Mailgun's PHP SDK in my laravel app, the emails are sent correctly after checking Mailgun's dashboard but I'm wondering if there's a way to get a confirmation programatically, currently I'm getting back an empty variable from my sendMail trait:

<?php
namespace App\Traits;


trait SendMail
{

    public function sendMail($view, $mailData, $subject, $to)
    {
        $html = view($view, compact('mailData'))->render();

        $result = app(Mailgun::class)->messages()->send(config('mail.mailgun.domain'), [
            'from' => config('mail.from.name').' <'.config('mail.from.address').'>',
            'to' => $to,
            'subject' => $subject,
            'html' => $html,
        ]);
        
        return $result;
    }

    
}

Here, result returns an empty array, always... how can I know if mail was sent with my code without checking Mailgun's dashboard or my mail inbox. Thanks in advance.

0 likes
4 replies
Sinnbeck's avatar

You need to use the api to check if the message was delivered successfully. Or setup a webhook to let them inform you when a message is delivered. Let me know what you wish to do and I can give you some pointers

I have both set up using https://github.com/Bogardo/Mailgun

Gabotronix's avatar

That package seems abandoned so I decided to use PHP SDK instead, all I want is to check if email was sent succesfully right after, is that even possible?

Sinnbeck's avatar

Yeah I know. I have been thinking about making a new one myself :)

No right after. Mailgun sometimes wait a little before sending (I often experience 1-2 minutes).

But I am sure you can just use their own package as well. Or setup a webhook. I found that easy to do and it works great!

Sinnbeck's avatar
Sinnbeck
Best Answer
Level 102

Here is my code if you want to set it up yourself (it just dispatches an even to the right place with the data I need). You can setup the webhooks on their website.

<?php

namespace App\Webhooks;

use App\Jobs\ParseMailDelivered;
use App\Jobs\ParseMailFailed;
use App\Models\Survey\EmailTemplate;

class MailgunWebhook
{
    /**
     * @var \App\Models\Survey\EmailTemplate
     */
    protected $email;

    /**
     * MailgunWebhook constructor.
     *
     * @param \App\Models\Survey\EmailTemplate $email Email template instance.
     */
    public function __construct(EmailTemplate $email)
    {
        $this->email = $email;
    }

    /**
     * Handle delivered message data
     *
     * @param array $data Incoming data.
     *
     * @return mixed
     * @throws \Exception Signature is invalid.
     */
    public function handleDelivered(array $data)
    {
        if (!$this->validate($data['signature'])) {
            throw new \Exception('Invalid signature!');
        }

        $event_data = $data['event-data'];
        $delivered_data = [
            'tags' => $event_data['tags'],
            'recipient' => $event_data['recipient'],
            'headers' => $event_data['message']['headers'],
            'timestamp' => $event_data['timestamp'],
        ];
        return ParseMailDelivered::dispatchNow($this->email, $delivered_data);
    }

    /**
     * Handle failed message data
     *
     * @param array $data Incoming data.
     *
     * @return mixed
     * @throws \Exception Signature is invalid.
     */
    public function handleFailed(array $data)
    {
        if (!$this->validate($data['signature'])) {
            throw new \Exception('Invalid signature!');
        }

        $event_data = $data['event-data'];
        $delivered_data = [
            'tags' => $event_data['tags'],
            'recipient' => $event_data['recipient'],
            'headers' => $event_data['message']['headers'],
            'timestamp' => $event_data['timestamp'],
            'delivery_status' => $event_data['delivery-status'],
            'severity' => $event_data['severity'],
        ];
        return ParseMailFailed::dispatchNow($this->email, $delivered_data);
    }

    /**
     * @param array  $signature
     * @param string $api_key
     *
     * @return bool
     */
    protected function validate(array $signature, $api_key = null)
    {
        $timestamp = $signature['timestamp'];
        $token = $signature['token'];
        $signature = $signature['signature'];
        //Concat timestamp and token values
        if (empty($timestamp) || empty($token) || empty($signature)) {
            return false;
        }
        $api_key = $api_key ? $api_key : config('mailgun.api_key');

        $hmac = hash_hmac('sha256', $timestamp.$token, $api_key);
        if (function_exists('hash_equals')) {
            // hash_equals is constant time, but will not be introduced until PHP 5.6
            return hash_equals($hmac, $signature);
        } else {
            return $hmac === $signature;
        }
    }
}

And the routes. Remember to disable CsrfToken check for the routes in VerifyCsrfToken (middleware)

Route::post('/mailgun/delivered', 'WebhookController@emailDelivered');
    Route::post('/mailgun/failed', 'WebhookController@emailFailed');
1 like

Please or to participate in this conversation.