Verifying Webhooks

With each webhook (for both Alerts/Events and webhooks used fulfilment) we send a signature field that can be used to verify the webhook was sent by Paddle.

We use public/ private key encryption to allow you to verify these requests. The information below should be a step-by-step guide on how to verify a Paddle signature.

1 Getting Your Public Key

We list your public key on your account settings page under the “Public Key” tab.

Your public key should look similar to the key below. (Note the key below is not your key.)

-----END PUBLIC KEY-----

2 Getting the Signature

Each webhook sends the signature that you’ll be verifying as a parameter called p_signature.

3 Verifying the Signature

The process of verifying the signature is simple. The signature is comprised of a signed set of all of the fields and their values within the request you have been sent. The first step is to remove the p_signature field, as that isn’t included within the signature verification. We then ksort() the fields so they’re in the same order, followed by serializing and signing the resulting array.

  // Your Paddle 'Public Key'
  $public_key = '-----BEGIN PUBLIC KEY-----
  // Get the p_signature parameter & base64 decode it.
  $signature = base64_decode($_POST['p_signature']);
  // Get the fields sent in the request, and remove the p_signature parameter
  $fields = $_POST;
  // ksort() and serialize the fields
  foreach($fields as $k => $v) {
	  if(!in_array(gettype($v), array('object', 'array'))) {
		  $fields[$k] = "$v";
  $data = serialize($fields);
  // Veirfy the signature
  $verification = openssl_verify($data, $signature, $public_key, OPENSSL_ALGO_SHA1);
  if($verification == 1) {
	  echo 'Yay! Signature is valid!';
  } else {
	  echo 'The signature is invalid!';

require 'base64'
require 'php_serialize'
require 'openssl'

public_key = '-----BEGIN PUBLIC KEY-----

# 'data' represents all of the POST fields sent with the request.
# Get the p_signature parameter & base64 decode it.
signature = Base64.decode64(data['p_signature'])

# Remove the p_signature parameter

# Ensure all the data fields are strings
data.each {|key, value|data[key] = String(value)}

# Sort the data
data_sorted = data.sort_by{|key, value| key}

# and serialize the fields
# serialization library is available here:
data_serialized = PHP.serialize(data_sorted, true)

# verify the data
digest    =
pub_key   =
verified  = pub_key.verify(digest, signature, data_serialized)

if verified
	puts "Yay! Signature is valid!"
	puts "The signature is invalid!"

# Your Paddle public key.
public_key = '''-----BEGIN PUBLIC KEY-----

import collections
import base64

# Crypto can be found at
from Crypto.Signature import PKCS1_v1_5
from Crypto.PublicKey import RSA
from Crypto.Hash import SHA
import hashlib

# PHPSerialize can be found at
import phpserialize

# Convert key from PEM to DER - Strip the first and last lines and newlines, and decode
public_key_encoded = public_key[26:-25].replace('\n', '')
public_key_der = base64.b64decode(public_key_encoded)

# input_data represents all of the POST fields sent with the request
# Get the p_signature parameter & base64 decode it.
signature = input_data['p_signature']

# Remove the p_signature parameter
del input_data['p_signature']

# Ensure all the data fields are strings
for field in input_data:
	input_data[field] = str(input_data[field])

# Sort the data
sorted_data = collections.OrderedDict(sorted(input_data.items()))

# and serialize the fields
serialized_data = phpserialize.dumps(sorted_data)

# verify the data
key = RSA.importKey(public_key_der)
digest =
verifier =
signature = base64.b64decode(signature)
if verifier.verify(digest, signature):
	print("Yay! Signature is valid!");
	print("The signature is invalid!");

Karl has kindly shared a Java helper class for webhook verification on Github and thanks to Drew for sharing a server side Swift gist.

Was this page helpful?