Receiving Amazon SNS Messages in PHP

Handling inbound Amazon SNS notification messages with PHP is simple. In this post, I'll show you how to retrieve data from incoming messages, and verify that the messages are coming from Amazon SNS.

A Little About Amazon SNS

Amazon Simple Notification Service (Amazon SNS) is a fast, fully-managed, push messaging service. Amazon SNS can deliver messages to email, mobile devices (i.e., SMS; iOS, Android and FireOS push notifications), Amazon SQS queues, and HTTP/HTTPS endpoints.

With Amazon SNS, you can setup topics to publish custom messages to subscribed endpoints. However, SNS messages are used by many of the other AWS services to communicate information asynchronously about your AWS resources. Some examples include:

  • Configuring Amazon Glacier to notify you when a retrieval job is complete.
  • Configuring AWS CloudTrail to notify you when a new log file has been written.
  • Configuring Amazon Elastic Transcoder to notify you when a transcoding job changes status (e.g., from "Progressing" to "Complete")

Though you can certainly subscribe your email address to receive SNS messages from service events like these, your inbox would fill up rather quickly. There is great power, however, in being able to subscribe an HTTP/HTTPS endpoint to receive the messages. This allows you to program webhooks for your applications to easily respond to various events.

Receiving a Message

In order for an HTTP/HTTPS endpoint to receive messages, you must subscribe the endpoint to an SNS topic. Before you do that, you need to create and deploy a script to the endpoint to process the messages.

Here is a naïvely simple PHP script that can read a posted SNS message.

<?php

// Fetch the raw POST body containing the message
$postBody = file_get_contents('php://input');

// JSON decode the body to an array of message data
$message = json_decode($postBody, true);
if ($message) {
    // Do something with the data
    echo $message['Message'];
}

The AWS SDK for PHP has an SNS Message class for representing an SNS message. It encapsulates the preceding code, and also validates the structure of the message data.

<?php

// Include Composer autoloader
require 'path/to/vendor/autoload.php';

// Create a message object from the POST body
$message = Aws\Sns\MessageValidator\Message::fromRawPostData();
echo $message->get('Message');

Amazon SNS sends different types of messages including SubscriptionConfirmation, Notification, and UnsubscribeConfirmation. The formats of these messages are described on the Appendix: Message and JSON Formats section of the Amazon SNS Developer Guide.

Confirming a Subscription to a Topic

In order to handle a SubscriptionConfirmation message, we need to add some code that actually does something with the message. SubscriptionConfirmation messages provide a URL that you can use to confirm the subscription. We'll use a Guzzle HTTP client to send a GET request to the URL.

$message = Aws\Sns\MessageValidator\Message::fromRawPostData();

// Create a Guzzle client and send a request to the SubscribeURL
$client = new Guzzle\Http\Client();
$client->get($message->get('SubscribeURL'))->send();

Verifying a SNS Message's Signature

Messages from Amazon SNS are signed. It's a good practice to verify the signature and ensure that a message was actually sent from Amazon SNS before performing actions as a result of the message. The SDK includes a MessageValidator class for validating the message, but you must have the OpenSSL PHP extension installed to use it.

use Aws\Sns\MessageValidator\Message;
use Aws\Sns\MessageValidator\MessageValidator;

$message = Message::fromRawPostData();

// Validate the message
$validator = new MessageValidator();
$validator->validate($message);

Handling Notifications

Let's put it all together and add some extra code for handling both SubscriptionConfirmation and Notification messages.

<?php

require 'path/to/vendor/autoload.php';

use Aws\Sns\MessageValidator\Message;
use Aws\Sns\MessageValidator\MessageValidator;
use Guzzle\Http\Client;

// Make sure the request is POST
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    die;
}

try {
    // Create a message from the post data and validate its signature
    $message = Message::fromRawPostData();
    $validator = new MessageValidator();
    $validator->validate($message);
} catch (Exception $e) {
    // Pretend we're not here if the message is invalid
    http_response_code(404);
    die;
}

if ($message->get('Type') === 'SubscriptionConfirmation') {
    // Send a request to the SubscribeURL to complete subscription
    (new Client)->get($message->get('SubscribeURL'))->send();
} elseif ($message->get('Type') === 'Notification') {
    // Do something with the notification
    save_message_to_database($message);
}

Conclusion

As you can see, receiving, verifying and handling Amazon SNS messages is simple. Setting up your application to receive SNS messages will allow you to create applications that can handle asynchronous communication from AWS services and other parts of your application.

EDIT: My next blog post is a follow up to this one, and describes how you can test your Amazon SNS webhooks locally.

Comments

There are no comments.