Set Up Webhooks
Create and configure a webhook endpoint to receive Fungies events.
This guide walks you through setting up a webhook endpoint to receive events from Fungies.
Quick Start
Create endpoint handler
Build an HTTP endpoint that accepts POST requests
Register in Dashboard
Add your endpoint URL in the Fungies Dashboard
Verify signatures
Validate that requests are from Fungies
1. Create Your Endpoint Handler
Your webhook endpoint needs to:
- Accept
POSTrequests with a JSON body - Return a
2xxstatus code quickly (before any heavy processing) - Handle the Event object payload
Return your 2xx response immediately, then process the event asynchronously. This prevents timeouts on long-running operations.
Basic Handler Example
// This example uses Express to receive webhooks
import express from "express";
const app = express();
app.post('/webhook', express.json({type: 'application/json'}), (request, response) => {
const event = request.body;
// Handle the event
switch (event.type) {
case 'payment_success':
// handlePaymentSuccess(event.data);
break;
case 'payment_refund':
// handlePaymentRefund(event.data);
break;
// ... handle other event types
default:
console.log(`Unhandled event type ${event.type}`);
}
response.json({success: true});
});
app.listen(8081, () => console.log('Running on port 8081'));2. Register in the Fungies Dashboard
Once your endpoint is ready (or you're using a tool like ngrok for local development):
- Go to Fungies Dashboard → Developers → Webhooks
- Click Create a webhook
- Configure your webhook:
| Field | Description |
|---|---|
| URL | Your endpoint URL (must be HTTPS in production) |
| Secret | A random string to sign events (save this securely) |
| Events | Select which event types to receive |
- Click Save
You can create multiple webhooks for different event types, or use a single endpoint that handles all events.
For subscription products, include payment_success if your handler fulfills orders. subscription_created alone is not enough — see Subscription checkouts and fulfillment.
3. Verify Webhook Signatures
Never skip signature verification in production. Without it, anyone could send fake events to your endpoint.
Fungies signs every webhook payload using HMAC-SHA256 with your webhook secret. The signature is included in the x-fngs-signature header:
x-fngs-signature: sha256_6808ed5be1262b60818359fa586145810d0793e8a677f1326520d3844e21b640How to Verify
- Get the raw request body (before any JSON parsing)
- Compute HMAC-SHA256 using your webhook secret
- Compare with the signature in the header
Verification Function
// This example shows standalone function to verify webhook event signature
import crypto from "crypto";
const verifyWebhookEventSignature = (payload: Buffer, signature: string, secret: string) => {
const hmac = crypto.createHmac("sha256", secret);
return `sha256_${hmac.update(payload.toString()).digest("hex")}` === signature;
};Express Middleware Example
// This example uses Express to receive webhooks
import express, { Response, Request } from "express";
import crypto from "crypto";
const WEBHOOK_SECRET = "<YOURE_SECRET_HERE>";
const app = express();
const verifyWebhookEventSignature = (request: Request, response: Response, buf: Buffer, encoding: string) => {
const signature = request.header('x-fngs-signature');
const hmac = crypto.createHmac("sha256", WEBHOOK_SECRET);
const calcSignature = `sha256_${hmac.update(buf.toString(encoding)).digest("hex")}`;
request["signature_match"] = (calcSignature === signature);
};
app.post('/webhook', express.json({
type: 'application/json',
verify: verifyWebhookEventSignature
}), (request, response) => {
if(!request["signature_match"]) {
return response.status(403).send('signature mismatch');
}
// Do something with the event
// ...
});
app.listen(8081, () => console.log('Running on port 8081'));Common Patterns
Idempotent Event Handling
Since webhooks guarantee at-least-once delivery, your handler might receive the same event multiple times. Use the event ID to prevent duplicate processing:
app.post('/webhook', async (req, res) => {
const event = req.body;
// Check if we've already processed this event
const processed = await db.events.findById(event.id);
if (processed) {
return res.status(200).send('Already processed');
}
// Process the event
await handleEvent(event);
// Mark as processed
await db.events.create({ id: event.id, processedAt: new Date() });
res.status(200).send('OK');
});Async Processing with Queues
For complex operations, queue events for background processing:
app.post('/webhook', async (req, res) => {
// Respond immediately
res.status(200).send('Received');
// Queue for async processing
await queue.add('process-webhook', req.body);
});Troubleshooting
| Issue | Solution |
|---|---|
| Not receiving events | Check your endpoint is publicly accessible and returns 2xx |
| Signature mismatch | Ensure you're using the raw body, not parsed JSON |
| Timeout errors | Return 2xx faster, process events asynchronously |
| Duplicate events | Implement idempotent handling using event IDs |
Next Steps
Test Your Webhooks
Learn how to test webhooks locally before deploying to production
Last updated