Validating Webhook Authenticity

Paysquad webhooks include a signature that allows you to verify the webhook was sent by Paysquad and not modified in transit.

Paysquad Webhook Validation Overview

Paysquad has an optional (although highly recommended) webhook validation process, which allows you to verify the webhook event was generated by Paysquad and has not modified. This follows a standard process:

  1. We use your webhook signing key value to encode the webhook body content
  2. We add the "HTTP_X_PAYSQUAD_SIGNATURE" header with the value of the encoded content to the webhook
  3. You need to read the "HTTP_X_PAYSQUAD_SIGNATURE" header, then encode it with the same process detailed below, then compare the encoded data is the same

The signature is created using a HMAC SHA-256 hash of the payload body with your webhook signing key, then base64-encoded.

How Signature Validation Works

  1. Payload
    Use the raw body of the webhook request as the payload.

  2. Signing Key
    Use your webhook signing key (available in the Paysquad merchant dashboard under Developer -> API Keys):

    This key is base64-decoded before use.

  3. Signature Generation

    • Create a HMAC SHA-256 hash of the payload using the decoded signing key.
    • Base64-encode the resulting hash.
    • Compare the generated signature to the X-Paysquad-Signature header.
  4. Validation

    • If the signatures match, the webhook is valid.
    • If not, reject the webhook and do not process the payload.

Example Webhook Signature Validation (PHP)

<?php

function validateWebhookAndGetPayload($webhookSigningKey)
{
    if (!isset($_SERVER['HTTP_X_PAYSQUAD_SIGNATURE'])) {
        throw new Exception("Missing signature in webhook request.");
    }

    $signature = $_SERVER['HTTP_X_PAYSQUAD_SIGNATURE'];
    $rawPostData = file_get_contents('php://input');

    // Decode the webhook signing key
    $decodedKey = base64_decode($webhookSigningKey);

    // Generate the expected signature
    $expectedSignature = base64_encode(hash_hmac('sha256', $rawPostData, $decodedKey, true));

    // Securely compare the signatures
    if (!hash_equals($expectedSignature, $signature)) {
        throw new Exception("Invalid signature in webhook request.");
    }

    // Parse the JSON payload
    $payload = json_decode($rawPostData, true);

    if (!isset($payload['paySquadId'])) {
        throw new Exception("Invalid payload: missing paySquadId.");
    }

    return $payload;
}

// Example usage:
// $payload = validateWebhookAndGetPayload('your_webhook_signing_key_here');
// Now safely process $payload
?>