Webhooks are the authoritative source of order status updates. All webhooks are HMAC SHA256-verified.
A webhook secret is required
Upgrading from an earlier version? As of 1.1.0, a delivery that arrives while no webhook secret is configured for that environment is rejected with HTTP 401. Earlier versions accepted unverified deliveries. If you never saved a webhook secret, webhooks stop working until you do — orders will sit in pending rather than completing.
As of 1.3.1 you usually do not have to copy the secret yourself: Register Webhook fills the matching Webhook Secret field in for you, because creating a destination is the one moment Paddle hands the signing secret over. Paste it manually only when the destination already existed or was made in the Paddle dashboard — updating an existing destination does not return a secret.
Secrets are per environment, so a store using both staging and production needs one saved for each. Both fields are password inputs, so a saved secret is not displayed back to you.
Endpoints
- Production:
/?wc-api=paddle_webhook - Staging:
/?wc-api=paddle_webhook_staging
Each environment has its own endpoint, so you can keep staging and production isolated. The endpoint a delivery arrives on — not the saved environment setting — decides which secret must verify it.
Setup
- Open WooCommerce → Settings → Payments → Paddle → Webhook Settings.
- Click Register Webhook to create the webhook in Paddle for the current environment. If a subscription already targets the same URL it is updated rather than duplicated.
- The signing secret returned by Paddle is saved into the Webhook Secret field for you, and the confirmation says so. If the destination already existed, no secret comes back — paste it from the Paddle dashboard into the matching Staging Webhook Secret or Production Webhook Secret field.
- Use Check Webhook to verify registration, or Forget Webhook to remove it. Forgetting also clears the stored signing secret for that environment, so nothing claims to be configured after the destination it belonged to is gone.
Registration is per environment — register separately for staging and production.
Handled events
transaction.completed— order marked complete.transaction.past_due— order marked failed. Orders that are already paid are never downgraded.transaction.canceled— order marked failed (thecancelledspelling is accepted too). Orders that are already paid are never downgraded.adjustment.createdandadjustment.updated— how refunds get recorded. An approved refund adjustment becomes a WooCommerce refund; a rejected one adds an order note.
Recognized but intentionally ignored: transaction.payment_failed (the customer can retry, so the order is not failed), transaction.created, transaction.ready, transaction.updated, and transaction.revised.
Subscriptions registered before 1.1.0 carry only the transaction events, which would mean refund approvals never reach the store. The plugin adds the missing adjustment events to an existing subscription automatically, and Check Webhook does the same on demand.
Webhook health panel
Below the webhook buttons, the settings screen shows a health panel for the current environment. It is built from state recorded locally as deliveries arrive, so opening the settings page never waits on the Paddle API.
- Status — a traffic light with a plain-language message and a suggested fix. It flags a missing webhook secret, a webhook that is not registered, a registration that has never received a delivery, a last delivery that was rejected or failed to process, and a store that has heard nothing for more than seven days.
- Facts — when the last delivery arrived, its event type and result, and a count of recently verified and rejected deliveries.
- Recent deliveries — "Show recent deliveries" fetches the 15 most recent notifications from Paddle on demand, showing event, status, time, and attempt count for each.
- Replay — asks Paddle to re-send a single delivery, which is quicker than waiting for a new event when you are debugging. Refresh the list afterwards to see the result.
Only a privacy-safe summary is recorded: timestamps, event type, result, a reason category, and counters that decay over time. No payloads, customer data, or identifiers are stored.
Security
- HMAC SHA256 signature verification on every webhook. Without a configured secret, deliveries are rejected rather than accepted unverified.
- Timestamp validation to prevent replay attacks.
- Timing-safe signature comparison via
hash_equals(). - Separate secrets per environment, with support for secret rotation.
AJAX actions (developer reference)
paddle_payment_complete— frontend payment completion callback; validates the transaction with the Paddle API before updating the order.paddle_register_webhook/paddle_check_webhook/paddle_forget_webhook— admin-only webhook management (capability + nonce protected).paddle_webhook_deliveries/paddle_replay_notification— recent deliveries and replay for the health panel (capability + nonce protected).