Webhooks
Receive real-time notifications when payment events occur. Webhooks allow you to automate order fulfillment and keep your system in sync.
How Webhooks Work
When a payment event occurs, PayHub sends an HTTP POST request to your configured webhook URL with details about the event. Your server should respond with a 2xx status code to acknowledge receipt.
Webhook Setup
Event Types
Subscribe to the events relevant to your integration:
| Event Type | Description |
|---|---|
payment.created | Payment was created |
payment.detected | Transaction detected on blockchain |
payment.confirming | Transaction is being confirmed |
payment.confirmed | Required confirmations reached |
payment.completed | Payment fully processed |
payment.expired | Payment expired without receiving funds |
payment.underpaid | Received less than expected amount |
payment.overpaid | Received more than expected amount |
Payload Structure
All webhook payloads follow the same structure:
{
"id": "evt_abc123xyz",
"type": "payment.confirmed",
"createdAt": "2024-01-15T12:00:00Z",
"data": {
"id": "pay_abc123xyz",
"status": "confirmed",
"amount": "100.00",
"currency": "USDC",
"network": "polygon",
"address": "0x1234...abcd",
"txHash": "0xabcd...1234",
"confirmations": 12,
"metadata": {
"orderId": "12345"
}
}
}Signature Verification
All webhooks include a signature in the x-payhub-signature header. Always verify this signature to ensure the webhook is genuinely from PayHub.
Security
Never process webhooks without verifying the signature first. This prevents attackers from sending fake webhook events to your endpoint.
Using the SDK
import { PayHubClient } from '@payhub/sdk';
import crypto from 'crypto';
const client = new PayHubClient({ apiKey: 'your-api-key' });
// Express.js example
app.post('/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-payhub-signature'];
const timestamp = req.headers['x-payhub-timestamp'];
// Verify signature
const isValid = client.webhooks.verifySignature({
payload: req.body,
signature,
timestamp,
secret: process.env.WEBHOOK_SECRET,
});
if (!isValid) {
return res.status(401).send('Invalid signature');
}
// Process the webhook
const event = JSON.parse(req.body);
handleWebhook(event);
res.status(200).send('OK');
});Manual Verification
// Manual signature verification
const crypto = require('crypto');
function verifySignature(payload, signature, timestamp, secret) {
const signedPayload = `${timestamp}.${payload}`;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}Retry Policy
If your endpoint returns a non-2xx status code or times out (30 seconds), we'll retry with exponential backoff:
After 10 failed attempts, the webhook is moved to the dead letter queue. You can manually replay failed webhooks from the Dashboard.
Best Practices
Return 200 quickly
Acknowledge the webhook immediately, then process asynchronously. Don't make the response wait for your business logic.
Handle duplicates
Use the event ID to deduplicate. The same event might be delivered multiple times in rare cases.
Verify before trusting
After receiving a webhook, call the API to verify the payment status before fulfilling orders.
Log everything
Log all incoming webhooks for debugging and audit purposes.