Webhooks & Signature Verification
Webhooks deliver real-time asynchronous notifications when payment statuses change (e.g. mobile money STK push confirmed, CBE Birr payment received).
π‘οΈ HMAC Signature & Verification Lifecycle
Section titled βπ‘οΈ HMAC Signature & Verification LifecycleβsequenceDiagram autonumber participant Provider as π± Telco / Bank Gateway participant Zirzir as β‘ Zirzir Rust Server participant Backend as π₯οΈ Your Webhook Handler
Provider->>Zirzir: Provider Webhook Callback Zirzir->>Zirzir: Validates provider payload & updates transaction Zirzir->>Zirzir: Computes HMAC-SHA256: t=timestamp, v1=HMAC(secret, "t.body") Zirzir->>Backend: POST /webhooks/zirzir with header X-Zirzir-Signature
alt Valid Signature & Timestamp within 300s Backend-->>Zirzir: 200 OK Backend->>Backend: Asynchronously fulfill customer order else Replay Attack or Invalid Secret Backend-->>Zirzir: 401 Unauthorized endπ Signature Header Format
Section titled βπ Signature Header FormatβEvery outbound webhook includes the X-Zirzir-Signature header:
X-Zirzir-Signature: t=1788319200,v1=52f08a478b273b5c3e5...t: Unix timestamp (in seconds). Protects against replay attacks.v1: Hex-encoded HMAC-SHA256 signature calculated over${t}.${raw_body}using your Webhook Secret.
π¦ Verifying with TypeScript SDK
Section titled βπ¦ Verifying with TypeScript SDKβimport { Zirzir } from '@zirzir/sdk';import express from 'express';
const app = express();const zirzir = new Zirzir({ baseUrl: 'http://localhost:8080', apiKey: 'zz_test_...' });
app.post('/webhooks/zirzir', express.raw({ type: 'application/json' }), (req, res) => { const signature = req.headers['x-zirzir-signature'] as string; const secret = process.env.ZIRZIR_WEBHOOK_SECRET!;
try { const event = zirzir.webhooks.constructEvent(req.body, signature, secret); console.log('β
Verified Event:', event.type, event.data.reference);
res.status(200).json({ received: true }); } catch (err) { console.error('β Signature Verification Failed:', err); res.status(400).send(`Webhook Error: ${(err as Error).message}`); }});