Webhooks allow your application to receive real-time notifications about events in your Payonify account, enabling you to automate workflows and keep your systems in sync with the payment lifecycle.
How Webhooks Work
sequenceDiagram
participant Customer
participant Your App
participant Payonify
participant Your Server
Customer->>Your App: Initiates payment
Your App->>Payonify: Create charge/checkout
Payonify-->>Your App: Returns response
Note over Payonify: Payment processed
Payonify->>Your Server: POST webhook event
Your Server->>Your Server: Verify signature
Your Server-->>Payonify: 200 OK
Your Server->>Your App: Update order status
Setting Up Webhooks
Navigate to the Webhooks section in your Payonify dashboard
Enter your webhook endpoint URL (must be HTTPS in production)
Copy your webhook secret (whsec_xxxx) for signature verification
Save your configuration
Keep Your Secret Safe
Never expose your webhook secret in client-side code or public repositories. If compromised, rotate it immediately from your dashboard.
Webhook Events
Payonify sends notifications for key payment lifecycle events:
For Phoenix apps, you'll need a custom body reader plug to preserve the raw request body for signature verification.
Handling Events
Respond Quickly
Your webhook endpoint should return a 200 status code as quickly as possible. Move any heavy processing to a background job queue.
Retry Policy
If your endpoint doesn't respond with a 2xx status code, Payonify will retry delivery with exponential backoff:
Attempt
Backoff
1st retry (attempt 2)
~4 seconds
2nd retry (attempt 3)
~8 seconds
3rd retry (attempt 4)
~16 seconds
4th retry (attempt 5)
~32 seconds
5th retry (attempt 6)
~1 minute
6th retry (attempt 7)
~2 minutes
7th retry (attempt 8)
~4 minutes
8th retry (attempt 9)
~8 minutes
9th retry (attempt 10)
~17 minutes
After 10 failed attempts, the webhook is marked as failed.
Idempotency
Handle Duplicates
Webhooks may be delivered more than once. Use the event id to ensure you don't process the same event twice.
Code
// Example: Track processed eventsconst processedEvents = new Set();async function handleWebhook(event) { if (processedEvents.has(event.id)) { return; // Already processed } // Process the event... processedEvents.add(event.id);}
For production, store processed event IDs in your database with an appropriate TTL.
Security Best Practices
Always verify signatures — Never skip verification in production
Use HTTPS — Only expose webhook endpoints over HTTPS
Validate timestamps — Reject webhooks older than 5 minutes