MailChimp API AJAX Subscribe Form



How To Add New Subscribers To A Mailchimp List With AJAX and PHP

AJAX forms are everywhere on the internet. Let's take a look at how to build one first before we dig into the hardening and all that Mailchimp stuff. We'll start with an HTML form with name and email fields.

HTML

<form id="subscribe">
  <input type="text" name="name" placeholder="Name" required autofocus>
  <input type="email" name="email" placeholder="Email" required>
  <button type="submit">Submit</button>
</form>

Which will give us a simple form to play with. Notice the required attribute on both fields and autofocus on the name. My guess is that you already know what these do by the name alone, if not... try it out!

Next we want to be able to submit the form and grab the data it contains. We'll have to import jQuery to do this. We can either download it ourselves and load it in or we can use a CDN (Content delivery network).

<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>

Let's catch the data...

  1. Bind a function to the form id (subscribe) on submit.
  2. prevent the form from submitting itself so that we can use the HTML5 form validation goodness before processing anything.
  3. Create a formData which compiles a set of key/value pairs from the field names from the form.
  4. Log the data so we can see it works.

JS

$('#subscribe').on('submit', function(e) {
  e.preventDefault();
  var formData = new FormData(e.target);
  var name = formData.get('name');
  var email = formData.get('email');
  console.log(`JS\nname: ${name}\nemail: ${email}`);
});

Now to send this data off to a PHP script.

JS

$('#subscribe').on('submit', function(e) {
  e.preventDefault();
  var formData = new FormData(e.target);
  var name = formData.get('name');
  var email = formData.get('email');
  console.log(`JS\nname: ${name}\nemail: ${email}`);

  $.ajax({
    type: "POST",
    url: 'subscribe.php',
    data: formData,
    processData: false,
    contentType: false,
    success: function(data) {
      console.log(data);
    },
    error: function(data) {
      console.log(data);
    }
  });

});

And the contents of said PHP file should be this, which will just return the form values for now.

PHP

<?php
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
  $name = $_POST['name'];
  $email = $_POST['email'];
  echo "PHP\nname: $name\nemail: $email";
}
?>

Which when submitted should output this gak in the dev console. One from the JS and another from the PHP.

Now we can do some trickery to stop as many bots as possible by adding a honeypot. Let's add a new field (surname), hide it with css and check if it gets filled with both JS and PHP just to make sure.

HTML

<form id="subscribe">
  <input type="text" name="name" placeholder="Name" required autofocus>
  <input type="text" name="surname" placeholder="Surname">
  <input type="email" name="email" placeholder="Email" required>
  <button type="submit">Submit</button>
</form>

CSS

  input[name='surname'] {
    display: none;
  }

JS

$('#subscribe').on('submit', function(e) {
  e.preventDefault();
  var formData = new FormData(e.target);
  var name = formData.get('name');
  var email = formData.get('email');
  var surname = formData.get('surname');
  if (surname.length) {
    console.log('JS\nBOT ALERT');
  } else {
    console.log(`JS\nname: ${name}\nemail: ${email}`);

    $.ajax({
      type: "POST",
      url: 'subscribe.php',
      data: formData,
      processData: false,
      contentType: false,
      success: function(data) {
        console.log(data);
      },
      error: function(data) {
        console.log(data);
      }
    });

  }
});

PHP

<?php
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
  $name = $_POST['name'];
  $email = $_POST['email'];
  $surname = $_POST['surname'];
  if (!strlen($surname)) {
    echo "PHP\nname: $name\nemail: $email";
  } else {
    echo "PHP\nBOT ALERT";
  }
}
?>

We can harden this further with PHP by checking the email using a built in PHP function called checkdnsrr which is pretty handy as it checks the DNS records for any host name or IP address. So let's split the email address, grab the host name and get it checked.

PHP

<?php
function is_valid_email($email)
{
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        return false;
    } else {
        $domain = explode("@", $email, 2);
        if (checkdnsrr($domain[1])) {
            return true;
        } else {
            return false;
        }
    }
}

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $name = $_POST['name'];
    $email = $_POST['email'];
    $surname = $_POST['surname'];
    if (!strlen($surname) && is_valid_email($email)) {
        echo "PHP\nname: $name\nemail: $email";
    } else {
        echo "PHP\nBOT ALERT";
    }
}

Now if a bot fills out the surname field or gives a dodgy email address we'll know about it and can act accordingly.

What now??? Learn how to send the form data to the Mailchimp API of course!

  1. Log into your Mailchimp account
  2. Go to Account -> Extras (dropdown) -> API Keys
  3. Create an API key and store it somewhere
  4. Go to the lists page /lists
  5. Got to settings of the list you want to use
  6. Grab the 'uniquie id for .....' number from the bottom of the page and store it somewhere

Note: You will have to add audience fields for the name to the list/audience settings. Add FNAME.

Now fill in the blanks (XXXX) with your keys.

PHP

$api_key = XXXX;
$list_id = XXXX;

And use this function to send the contact details to the Mailchimp list.

PHP

function send_to_mailchimp($mailchimp_name, $mailchimp_email, $api_key, $list_id)
{
    $prefix =  explode("-", $api_key)[1];
    $api_url = "https://$prefix.api.mailchimp.com/3.0/lists";

    $post_data = array(
      "email_address" => $mailchimp_email,
      "status" => "subscribed",
      "merge_fields" => array("FNAME" => $mailchimp_name)
    );

    $ch = curl_init("$api_url/$list_id/members/");
    curl_setopt_array($ch, array(
      CURLOPT_POST => true,
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_HTTPHEADER => array(
          "Authorization: apikey $api_key",
          "Content-Type: application/json"
      ),
      CURLOPT_POSTFIELDS => json_encode($post_data)
    ));

    $response_data = json_decode(curl_exec($ch));
    if ($response_data->title === "Member Exists") {
        return false;
    } elseif ($response_data->status === "subscribed") {
        return true;
    } else {
        return false;
    }
}

And the rest of it hooked in to send to Mailchimp...

PHP

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $name = $_POST['name'];
    $email = $_POST['email'];
    $surname = $_POST['surname'];
    if (!strlen($surname) && is_valid_email($email)) {
        if (send_to_mailchimp($name, $email, $api_key, $list_id)) {
            echo "PHP - SUCCESS\nname: $name\nemail: $email";
        } else {
            echo "PHP - FAIL\nname: $name\nemail: $email";
        }
    } else {
        echo "PHP\nBOT ALERT";
    }
}

If all went to plan you now should see something like this in the dev console and a new contact added to the Mailchimp list. You probably want to do something with the AJAX success function now. Remember that this is all just an example to show you how it works!

Thanks for reading. x

Resources