Skip to content
All posts
LaravelAI

Real Email Validation using Mailgun in Laravel

July 22, 2022·Read on Medium·

Email is one of the most preferred and effective communication channels, with most people checking their emails daily. Study said only 10% of email addresses collected are accurate which is mostly due to human error. As a developer or the owner of the system, email validation are one of the important things to do especially when the system have registration form.

Laravel already provides features with a wide options when it comes to email validation such as :-

  • RFC Standard validation (rfc, strict)
  • DNS Check Validation (dns)
  • Spoof Check Validation (spoof)
  • Email Filter Validation (filter)

By default, Laravel email validation doesn’t come with real email validation upfront. Even though we can send an email with OTP or links to verify, it would require the system to store the data temporarily in the database and mark it as unverified for a while.

Here come Mailgun Verification API. Mailgun Verification API will verify the given address based on:

  • Mailbox detection
  • Syntax checks (RFC defined grammar)
  • DNS validation
  • Spell checks
  • Email Service Provider (ESP) specific local-part grammar (if available).

BUT, this API come with price. Pricing details for Mailgun’s email verification service can be found on our pricing page.

Here we will try to implement it in Laravel as the validation rule for the email. Before we started, it required you to register an account.

Lets get started

If we open the Mailgun api documentation,

We can trigger this verification API through GET request with /v3/address/validate

curl -G --user 'api:pubkey-XXXXXX' -G \
https://api.mailgun.net/v3/address/validate \
--data-urlencode address='foo@mailgun.net'

If you preferred POST request,

curl --user 'api:pubkey-XXXXX' -X POST
https://api.mailgun.net/v3/address/validate
-F address='foo@mailgun.net'

Here are the sample response if the email is exactly exist

{
"address": "foo@mailgun.net",
"did_you_mean": null,
"is_disposable_address": false,
"is_role_address": true,
"is_valid": true,
"mailbox_verification": "true",

"parts": {
"display_name": null,
"domain": "mailgun.net",
"local_part": "foo"
}
}

If email not actually exist

{
"address":"foo@terato1.com",
"did_you_mean":null,
"is_disposable_address":false,
"is_role_address":false,
"is_valid":false,

"mailbox_verification":"false",
"parts":{
"display_name":null,
"domain":"terato1.com",
"local_part":"foo"
},
"reason":"No MX records found for domain 'terato1.com'"
}

Now we know how the request and response from the API. Note that, the API need a public key. You can get from Mailgun Account (Required) at API Key section.

After you got the public key, alter your .env file and put this line

MAILGUN_PUBLIC_KEY=pubkey-XXXXXXX

Adjust the mail.php configuration file and put new field

'mailgun' => [
'transport' => 'mailgun',
'public_key' => env('MAILGUN_PUBLIC_KEY')
],

Now we can create a Rule in Laravel. Run rule command,

php artisan make:rule RealEmailValidation

If we refer from the response, 3 field we should take reconsider : — is_valid, mailbox_verification and is_disposable_address

is_valid returns true when an address is parsable, passes known grammar checks and an SMTP server is present.

mailbox_verification attribute will return true if the email is valid, false if the email was invalid, or unknown if the SMTP request could not be completed.

is_disposable_address return true if the domain is in a list of disposable email addresses.

So, final validation rule class will be look like this,

RealEmailValidation.php View on GitHub
<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class RealEmailValidation implements Rule
{
    /**
     * Create a new rule instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        try {
            $response = $this->mailgunValidation($value);
        } catch (\Exception) {
            return false;
        }

        if ($response->successful()) {
            if (!$response->json('mailbox_verification')) {
                return false;
            }

            if ($response->json('is_disposable_address')) {
                return false;
            }

            return $response->json('is_valid');
        }

        return false;
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return 'Unable to verify the existence of the email.';
    }

    private function mailgunValidation($email): \GuzzleHttp\Promise\PromiseInterface|\Illuminate\Http\Client\Response
    {
        return \Http::withBasicAuth('api', config('mail.mailers.mailgun.public_key'))
            ->asForm()->post("https://api.mailgun.net/v3/address/validate", [
                'address' => $email
            ]);
    }
}

Found this helpful?

If this article saved you time or solved a problem, consider supporting — it helps keep the writing going.

Originally published on Medium.

View on Medium