Handling Webhooks
Receive real-time payment events from any provider, verify their authenticity, and process them reliably with PayKit's unified webhook layer.
Why Webhooks?
Payment providers send webhooks to notify your server about events such as successful payments, failed charges, and completed refunds. Webhooks are far more reliable than client-side callbacks or polling because they are delivered server-to-server, ensuring your application stays in sync with the provider even when the customer closes their browser mid-payment.
Setting Up
Follow these three steps to start receiving webhooks:
- Configure an endpoint in your payment provider's dashboard (e.g.
https://example.com/api/webhooks/stripe). - Copy the webhook secret provided by the dashboard and store it in your environment variables.
- Add a POST handler at the matching path in your server to receive and process events.
Verifying Signatures
Every webhook request includes a cryptographic signature. Use paykit.webhooks.construct to verify the signature and parse the event in one step:
Always verify webhook signatures. Without verification, an attacker could forge webhook payloads and trick your server into fulfilling fraudulent orders. Never skip this step in production.
Unified Events
PayKit normalises provider-specific event names into a consistent set of unified events so your handler code stays provider-agnostic:
| PayKit Event | Stripe Event | Razorpay Event |
|---|---|---|
payment.succeeded | payment_intent.succeeded | payment.captured |
payment.failed | payment_intent.payment_failed | payment.failed |
refund.created | charge.refunded | refund.processed |
payment.updated | payment_intent.updated | payment.updated |
Multi-Provider Webhooks
If your application accepts payments from multiple providers, you have two options for structuring your webhook endpoints.
Separate Endpoints
Create a dedicated route for each provider. This is the recommended approach because each provider uses a different signature scheme:
Single Endpoint with Provider Detection
Alternatively, use a single endpoint and detect the provider from the request headers:
Testing Locally
Webhooks require a publicly accessible URL. During local development, use a tunnel to forward events to your machine.
ngrok
Copy the generated https:// URL and paste it into your provider's webhook dashboard.
Stripe CLI
Stripe provides a dedicated CLI for forwarding test webhooks:
Razorpay
Razorpay offers a webhook simulator in the dashboard under Account Settings → Webhooks → Test. Enter your ngrok URL and select the event type to send a test payload.
Idempotency
Providers may deliver the same webhook more than once. Always check whether an event has already been processed before acting on it:
Error Handling
Return a 2xx status code to acknowledge successful receipt. If your handler throws or returns a 5xx, most providers will retry the delivery with exponential back-off. Wrap your handler in a try/catch to avoid accidental retries: