Webhooks
Webhooks are managed from the PekoPay portal Webhook Settings page.
Configure:
- Destination URL
- Signing secret
- Event subscriptions
Payload envelope
{
"type": "customer:created",
"businessId": "<tenantId>",
"payload": {
"customer_id": "..."
}
}Fields:
type: event namebusinessId: tenant/business identifierpayload: event-specific data
Event types
Customer lifecycle:
customer:createdcustomer:updatedcustomer:deleted
Product lifecycle:
product:createdproduct:updatedproduct:deleted
Subscription lifecycle:
subscription:createdsubscription:updatedsubscription:deleted
Subscription charge lifecycle:
subscription:charge:successsubscription:charge:failed
Invoice lifecycle:
invoice:createdinvoice:updatedinvoice:deleted
API as source of truth
Treat webhook payloads as signals, not authoritative state snapshots.
Recommended pattern:
- Verify signature.
- Persist raw event and metadata.
- Acknowledge quickly (
2xx). - Process asynchronously.
- Re-fetch the latest state from API before updates.
- Apply idempotent writes in your system.
Resource re-fetch mapping:
customer:*->GET /tenants/:tenantId/customers/:idproduct:*->GET /tenants/:tenantId/products/:idsubscription:*andsubscription:charge:*->GET /tenants/:tenantId/subscriptions/:idinvoice:*->GET /tenants/:tenantId/transactions
Signature verification
Header:
X-Webhook-Signature=hex(HMAC_SHA256(rawBody, secret))
import crypto from 'crypto';
import express from 'express';
const app = express();
const webhookSecret = process.env.WEBHOOK_SECRET!;
app.post('/webhooks/pekopay', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.header('X-Webhook-Signature') || '';
const rawBody = req.body as Buffer;
const expected = crypto.createHmac('sha256', webhookSecret).update(rawBody).digest('hex');
if (signature !== expected) {
return res.status(401).send('Invalid signature');
}
const payload = JSON.parse(rawBody.toString('utf8'));
return res.status(200).send('OK');
});Last updated on