Webhook Development
This page covers developer-facing details for building against PayRequest’s webhook — signature verification in more than one language, local testing, and how to build a resilient receiver given the delivery model. Start with Webhook Configuration first if you haven’t set up your webhook URL yet — this page assumes you already have a URL and signing secret configured.PayRequest currently sends one event,
payment.succeeded, to a single webhook URL per account. Everything below builds on that.Signature Verification in Other Languages
Every request carries anX-PayRequest-Signature header formatted as sha256={hmac} — an HMAC-SHA256 of the raw request body, signed with your webhook secret. The PHP example is in Webhook Configuration; here’s the same check in Node.js and Python.
Node.js (Express)
Idempotency
With a single event type today, duplicate-processing risk is lower than on platforms with dozens of event types, but it’s still worth guarding against — a network retry on your own infrastructure, a webhook.site relay, or a manual reconciliation script could all deliver the samepayment.succeeded event more than once.
- Use
data.id(the PayRequest transaction ID) as your idempotency key - Before acting on an event, check whether you’ve already processed that transaction ID
- Because delivery isn’t automatically retried by PayRequest (see below), you’re more likely to see a missing event than a duplicate one — but your handler should tolerate both
Delivery Model (Recap)
As covered in Webhook Configuration: dispatch is asynchronous via a queued job, requests time out after 10 seconds, and delivery is best-effort — there’s no automatic retry if your endpoint is down or errors. Don’t build an integration that assumes every payment will arrive as a webhook; reconcile periodically against the API or dashboard as a safety net, especially for anything revenue-critical.Testing Locally
There’s no built-in test-send button, so the practical way to develop against the webhook is to trigger a real payment (testmode where your provider supports it) and inspect what arrives.Expose your local server
Use ngrok (
ngrok http 3000) or a similar tunnel to get a public HTTPS URL pointing at your local dev server.Or inspect payloads first with webhook.site
Before wiring up real handler code, point your webhook URL at a fresh webhook.site endpoint to see the exact payload shape and headers PayRequest sends.
Set the URL in PayRequest
Go to Settings → API Tokens, paste your ngrok or webhook.site URL, and save.
Trigger a payment
Complete a real (or testmode) payment against your account and confirm the request arrives, the signature verifies, and your handler processes it correctly.
FAQ
Should I process the webhook synchronously or queue it on my end?
Should I process the webhook synchronously or queue it on my end?
Queue it. Your endpoint has 10 seconds to respond before PayRequest’s request times out — do the minimum work needed to acknowledge receipt (verify signature, store/queue the event) and process the business logic asynchronously on your side.
Can I filter which events I receive?
Can I filter which events I receive?
Not currently — there’s only one event type (
payment.succeeded), so there’s nothing to filter yet.What should my endpoint return?
What should my endpoint return?
A 2xx status code to acknowledge receipt. Since PayRequest doesn’t automatically retry failed deliveries, returning an error won’t trigger a redelivery — it will just be logged as failed on PayRequest’s side.
Next Steps
Webhook Configuration
Set up your webhook URL and see the full payload reference
API Reference
Reconcile transaction state directly instead of relying solely on webhooks