The third step to create Square webhooks is to have your application verify the event notification and validate the data included.
Webhooks

Step 3: Verify and Validate an Event Notification

You can verify the creation and receipt of an event notification using either a test endpoint you create or a public site such as webhook.site. You can use API Explorer to generate events that webhooks can subscribe to. For more information about using API Explorer, see Testing with API Explorer.

To verify your event notification subscription using webhook.site:

  1. Go to webhook.site in a browser, copy the provided unique URL to the clipboard, and leave the page open.

  2. Create a webhook using Step 2: Subscribe to Event Notifications.

  3. Use the unique URL you copied from webhook.site for your notification URL, and then choose Select All under Events.

  4. Create an event in API Explorer. Creating a customer using the Customers API is a simple way to create an event because it requires only one of the five identity values, such as family name (last name), a given name (first name), or an email address. This generates the customer.created event.

  5. Return to the webhook.site page to view the event notification.

After you verify that your event notification subscription is working, you need to add code to your notification URL so that your application can process the event information. Because your notification URL is public and can be called by anyone, you must validate each event notification to confirm that it came from Square. A non-Square post can potentially compromise your application. All webhook notifications from Square include an x-square-signature header. The value of this header is an HMAC-SHA1 signature generated using your webhook signature key, the notification URL, and the raw body of the request. You can validate the webhook notification by generating the HMAC-SHA1 in your own code and comparing it to the signature of the event notification you received.

Important

A malicious agent can compromise your notification endpoint by using a timing analysis attack to determine the key you are using to decrypt and compare webhook signatures. You should use a constant-time crypto library to prevent such attacks by masking the actual time taken to decrypt and compare signatures.

The following functions generates an HMAC-SHA1 signature from your signature key, the notification URL, and the event notification body. You can then compare the result with the event notification's x-square-signature.

Web event notification validation - JavaScript Permalink Get a link to this section

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
// The crypto module provides cryptographic functionality.
const crypto = require('crypto');
const http = require('http');

// The URL where event notifications are sent.
const NOTIFICATION_URL = 'https://example.com/webhook';

// The event notification subscription signature key (sigKey) defined in dev portal for app.
const SIG_KEY = '<SIGNATURE_KEY>';

// Function to generate signature from url and body and compare to square signature.
function isFromSquare(sigKey, notificationUrl, squareSignature, rawBody) {
  // create hmac signature
  const hmac = crypto.createHmac('sha1', sigKey);
  hmac.update(notificationUrl + rawBody);
  const hash = hmac.digest('base64');

  // compare to square signature
  return hash === squareSignature;
}

// A generic request handler to get the raw bytes of the request body.
// Different frameworks may provide the raw request body in other ways.
function requestHandler(request, response) {
  let rawBody = '';
  request.setEncoding('utf8');

  request.on('data', function(chunk) {
    rawBody += chunk;
  });

  request.on('end', function() {
    const squareSignature = request.headers['x-square-signature'];
    const eventIsFromSquare = isFromSquare(SIG_KEY, NOTIFICATION_URL, squareSignature, rawBody);

    if (eventIsFromSquare) {
      response.writeHead(200);
      response.write("Signature is valid.\n");
    } else {
      response.writeHead(400);
      response.write("Signature is not valid.\n");
    }
    response.end();
  });
}

Webhook event notification validation - Python Permalink Get a link to this section

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
# python 3 compatible only
from http.server import BaseHTTPRequestHandler, HTTPServer
import base64
import hmac
import json

# The URL where event notifications are sent.
NOTIFICATION_URL = 'https://example.com/webhook'

# The event notification subscription signature key (sigKey) defined in dev portal for app.
SIG_KEY = b'asdf1234';

# A generic request handler to get the raw bytes of the request body.
# Different frameworks may provide the raw request body in other ways.
class MainHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        length = int(self.headers.get('content-length', 0))
        body = self.rfile.read(length)
        square_signature = self.headers.get('x-square-signature')
        event_is_from_square = self.is_from_square(SIG_KEY, NOTIFICATION_URL, square_signature, body)
        if event_is_from_square:
            self.wfile.write(b'Signature is valid.\n')
            self.send_response(200)
        else:
            self.wfile.write(b'Signature is not valid.\n')
            self.send_response(400)

        self.end_headers()

    # Method to generate signature from url and body and compare to square signature.
    def is_from_square(self, sig_key, notification_url, square_signature, body):
      # convert url to bytes
      url_request_bytes = notification_url.encode('utf-8') + body

      # create hmac signature
      hmac_code = hmac.new(sig_key, msg=None, digestmod='sha1')
      hmac_code.update(url_request_bytes)
      hash = hmac_code.digest()

      # compare to square signature from header
      return base64.b64encode(hash) == square_signature.encode('utf-8')

# Simple server for local testing. Run the server with python3 and send the following curl from a separate terminal:
# curl -vX POST localhost:8000 -d '{"hello":"world"}' -H "X-Square-Signature: KiPKaeNj311k3uhWDUbESP1QTRM="
server = HTTPServer(("0.0.0.0", 8000), MainHandler)
server.serve_forever()

Webhook event notification validation - C# Permalink Get a link to this section

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
using Microsoft.AspNetCore.Http;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

public class SquareWebhooksExample
{
    /** The URL where event notifications are sent. */
    const string NOTIFICATION_URL = "https://example.com/webhook";

    /** The event notification subscription signature key (sigKey) defined in dev portal for app. */
    const string SIGNATURE_KEY = "asdf1234";

    readonly byte[] secret = Encoding.UTF8.GetBytes(SIGNATURE_KEY);

    private async Task<string> GetNotificationUrl(HttpRequest request)
    {
        using (var reader = new StreamReader(request.Body, Encoding.UTF8))
        {
            var value = await reader.ReadToEndAsync();
            return $"{NOTIFICATION_URL}{value}";
        }
    }

    /**
     * Generate signature from url and body and compares to Square signature.
     */
    public async Task<bool> IsFromSquare(HttpRequest request)
    {
        var signature = request.Headers["x-square-signature"];
        var payload = await GetNotificationUrl(request);
        var payloadBytes = Encoding.UTF8.GetBytes(payload);

        using (var hmac = new HMACSHA1(secret))
        {
            var hash = hmac.ComputeHash(payloadBytes);
            var hashString = Convert.ToBase64String(hash);
            return hashString.Equals(signature);
        }
    }
}

Webhook event notification validation - Go Permalink Get a link to this section

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
package main

import (
    "crypto/hmac"
    "crypto/sha1"
    "encoding/base64"
    "encoding/json"
    "fmt"
)

func main() {
    // Copy/paste into goplay.space
    sigKey := "SIGNATURE KEY"
    url := "NOTIFICATION URL"
    event := `{"webhook":"event"}`
    expectedSig := "EXPECTED SIGNATURE"

    payload := new(bytes.Buffer)
    json.Compact(payload, []byte(event))

    appended := append([]byte(url), payload.Bytes()...)
    key := []byte(sigKey)
    hash := hmac.New(sha1.New, key)
    hash.Write(appended)
    sig := base64.StdEncoding.EncodeToString(hash.Sum(nil))

    fmt.Println(sig)
    fmt.Println(sig == expectedSig)
}

If you need more assistance, contact Developer Support or ask for help in the Developer Forums.