Here's a quick snippet that will save developers using Paddle and Elixir many hours of frustration (trust me, I suffered through them).
This function validates the raw data of a webhook request from Paddle.com. It's based on the code officially provided here.
It requires one dependency, php_serialize.
@doc """ Verifies a Paddle webhook connection. https://developer.paddle.com/webhook-reference/verifying-webhooks """ def verify_webhook(raw) do # We take the 'data' key from the response. signature = Base.decode64!(Map.get(raw.data, :p_signature, "")) # This requires an ordered map to validate # Maps in Elixir are ordered under n<32 items # We assume that the length is less than 32 # Otherwise, just use an OrdMap implementation raw = Map.get(raw, :data) |> Map.delete(:p_signature) |> Map.new(fn {k, v} -> {k, to_string(v)} end) # Paddle weirdly requires this format to validate their requests... serialized = PhpSerializer.serialize(raw) # We get the Public Key from the application settings. str_pkey = Application.get_env(:cashflow, Cashflow.Billing)[:paddle_pkey] [decoded_pkey] = :public_key.pem_decode(str_pkey) decoded_pkey = :public_key.pem_entry_decode(decoded_pkey) :public_key.verify(serialized, :sha, signature, decoded_pkey) end
To use this, either hard-code the public key or open your application's config and add an entry:
# Make sure to include the new lines config :app, App.Billing, paddle_pkey: "-----BEGIN PUBLIC KEY------..."
All done. You're all set to start charging people for stuff!