Subscribing Users to a MailChimp List with Laravel and Guzzle

Managing users and mailing lists are not a that painful nowadays. Since we have excellent services that can cover all the needs we have, we don’t need to worry about it too much. But still, let’s take a look at how can we handle user subscriptions when Laravel fires different events.

Getting Started

First of all, we must clarify that the way we present the solutions is not GDPR compatible. The users have to confirm any actions when their data is getting processed. Just keep it in mind.

Also, if you need a fully working solution for your newsletters, you may use the Spatie package.

But for now, let’s say, we want to subscribe a user to the mailing list when it registers to the site. By default, Laravel fires the Illuminate\Auth\Events\Registered event when a user registers to our application. It means, writing a listener to this event would be enough to handle the subscription. Then, let’s move on and make that listener!

Making and Registering the Listener

With the help of the php artisan make:listener command we can easily create the event listener. Let’s name it to SubscribeUser.

Before we write the logic, we need to register the listener for the event we mentioned before. In the EventServiceProvider‘s $listen property we can pair them easily.

protected $listen = [
    // ...
    
    \Illuminate\Auth\Events\Registered::class => [
        \App\Listeners\SubscribeUser::class,
    ],
    
    // ...
];

Now whenever the registered event is being fired, we can listen to it with our listener.

Handling the Subscription in the Listener

MailChimp has a nice documentation of its API. Currently, we need the members API because we only want to subscribe a user to the list. As we see, we need to send a POST request to the given endpoint with the required credentials.

For the POST request, we will use Guzzle. We can install it with the composer require guzzlehttp/guzzle command. After it’s done we need to grab all the credentials we need: username, API key, list ID. Since the API key contains the data center we use, we can use the key to grab that information.

We assume you store your credentials in the .env file. Also, as a best practice, we use the env() function to access these credentials in our config files. Then we use the config() helper application-wide to grab the credentials. In this case, we will store these in the services config file.

So, we prepared everything, now let’s see the code itself:

 <?php

namespace App\Listeners;

use GuzzleHttp\Client;
use Illuminate\Queue\InteractsWithQueue;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Contracts\Queue\ShouldQueue;

class SubscribeUser implements ShouldQueue
{
    use InteractsWithQueue;

    protected $client;
    protected $list;
    protected $dc;
    protected $token;

    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
        $this->client = new Client;

        $this->list = config('services.mailchimp.list');
        $this->dc = collect(explode('-', config('services.mailchimp.key')))->last();
        $this->token = base64_encode(sprintf('%s:%s', config('services.mailchimp.user'), config('services.mailchimp.key')));
    }

    /**
     * Handle the event.
     *
     * @param  \Illuminate\Auth\Events\Registered  $event
     * @return void
     */
    public function handle(Registered $event)
    {
        try {
            $this->client->post("https://{$this->dc}.api.mailchimp.com/3.0/lists/{$this->list}/members", [
                'headers' => [
                    'Authorization' => "Basic {$this->token}",
                    'Content-type' => 'application/json',
                ],
                'json' => [
                    'status' => 'subscribed',
                    'email_address' => $event->user->email,
                    'merge_fields' => ['FNAME' => $event->user->name, 'LNAME' => ''],
                ],
            ]);
        } catch (ClientException $e) {
            // Handle errors
        }
    }
}

So what happens here? In the constructor, we initialize a new Client instance. We will use this to send the POST request to the endpoint. Also, we grab the list ID from the config. Then we extract the data center (dc) from the API key.

The anatomy of the API key looks like this: key + “-” + data center. It means we can explode the API key by a dash and pop the last result, that will be the data center we use. Example key: biia9wse9lq37bc788ebf3a849cd22fb1-us11.

Then we need to set the token we want to send in the header. As you see, it’s the base64 encoded format of the username + “:” + API key.

After we done with setting up the credentials, we can move on the handle method. We put the whole code in a try-catch block, in case we want to handle the errors. Then with the client, we send the request to the endpoint with the requested body and headers.

Please note, we use the InteractsWithQueue trait and the ShouldQueue interface. Since this action can take some time, it’s a good idea to queue it. Then the user does not need to wait and we can process the subscription behind the scenes.

If the request succeeded, we can see the user as a subscribed member in our MailChimp dashboard.

Summary

It’s nice how easily can we perform actions like this. Of course in real life, we need a bigger thing (like the package above) to fully handle the needs. But it’s a nice example that we can easily integrate services with Laravel.

Special thanks for the following recource(s): Icon made by Swifticons from www.flaticon.com