Skip to main content

Webhook Signature Verification

When you configure a webhook secret for a non-Stripe donation platform, Benecaster verifies incoming webhook payloads before logging the donation. This page explains how to obtain each platform’s credential and how verification behaves once it’s configured.

Stripe donations are verified separately via Stripe’s own HMAC signature mechanism — this page covers Ko-fi, PayPal, and Buy Me a Coffee only.


Obtaining Your Platform Credentials

Ko-fi

  1. Log in to Ko-fi and go to More → API.
  2. Under Webhook URL, find the Verification Token field.
  3. Copy the token and paste it into Settings → Listener Support → Webhooks → Ko-fi verification token in Benecaster.

Ko-fi sends the token in an X-KoFi-Token header on every webhook request. Benecaster verifies it with a constant-time string comparison.

PayPal

  1. In the PayPal Developer Dashboard, open My Apps & Credentials and select your app.
  2. Under Webhooks, add a webhook with this URL, putting your show’s ID in place of YOUR_SHOW_ID: https://your-site.com/wp-json/benecaster/v1/listener-support/donations?show_id=YOUR_SHOW_ID&platform=paypal PayPal’s events don’t say which show they are for, so the URL has to.
  3. Subscribe it to Payment capture completed. If your donate button is an older PayPal button, subscribe to Payment sale completed as well.
  4. Copy the webhook’s Webhook ID (the ID string, not the URL).
  5. Paste it into Settings → Listener Support → Webhooks → PayPal webhook ID in Benecaster.

PayPal signs every webhook with its own certificate, and Benecaster checks that signature against your webhook ID before recording anything. It only accepts a certificate served from PayPal’s own API addresses.

What gets recorded. Each completed payment is recorded once, with its amount, currency and PayPal transaction ID; if PayPal sends the same payment again, it is not recorded twice. Any other event PayPal sends is acknowledged and ignored. PayPal’s payment events carry no donor name or email address, so these donations appear without a name and no thank-you email is sent.

Saving the webhook ID also keeps your PayPal donations clear of the hand-reported limit. Benecaster accepts at most five unverified donation reports per hour from any one address; a PayPal delivery that passes the signature check doesn’t count toward that limit, so a busy show’s webhooks are never turned away.

Buy Me a Coffee

  1. Log in to Buy Me a Coffee and go to Extras → Webhooks.
  2. Under your webhook configuration, find the Secret field.
  3. Copy the secret and paste it into Settings → Listener Support → Webhooks → Buy Me a Coffee webhook secret in Benecaster.

Buy Me a Coffee sends the secret in an X-BMAC-Token header. Benecaster verifies it with a constant-time string comparison, the same shape as Ko-fi.


How Verification Behaves Once Configured

Requests with a configured secret are verified. When a webhook arrives from a platform whose secret you’ve saved, Benecaster checks the signature before writing the donation row.

  • Verification passes — the donation is logged normally. The benecaster_donation_webhook_verified action fires.
  • Verification fails — Benecaster returns HTTP 403 with the error code signature_invalid and the message “Webhook signature verification failed.” The donation is not logged. The benecaster_donation_webhook_rejected action fires. A single debug log line is written: [benecaster] webhook verification failed for platform=<canonical> from IP=<hash> (the IP is hashed — raw addresses are never logged).

Requests from platforms without a configured secret are accepted silently. Verification is strictly opt-in. If you haven’t saved a secret for Ko-fi, Ko-fi webhook requests continue to pass through and log donations as before, with no verification and no action fired. The presence of the benecaster_donation_webhook_verified action firing is itself the signal that verification actually ran for a given request.

Platform slug normalization is permissive. Variations like Ko-Fi, ko_fi, kofi, and KoFi all resolve to the same canonical kofi slug internally. You don’t need to match the platform field in your webhook payload to an exact string.

Stripe payloads bypass this path entirely. Stripe webhooks are received and verified at a separate endpoint (/stripe-webhook) using Stripe’s own timestamp-tolerant HMAC path. They do not pass through the donation webhook endpoint and are unaffected by the secrets configured here.


Developer Hooks

Two action hooks let you observe verification outcomes:

  • benecaster_donation_webhook_verified( string $canonical_platform, string $raw_platform ) — fires on successful verification. Only fires when a secret was configured; silent pass-throughs (no configured secret) do not fire this hook.
  • benecaster_donation_webhook_rejected( string $canonical_platform, string $raw_platform ) — fires on 403 rejection. Use this to route alerts to Slack, log to an external service, or trigger security monitoring.

See the individual hook pages for signatures and usage examples.

See Also

Need this built rather than just documented? See our services →