E-commerce8 min read

Wave Business Webhooks: Automating Payment Confirmation and Reconciliation in Senegal

Mohamed Bah·Fondateur, Kolonell
June 9, 2026
Share:
Wave Business Webhooks: Automating Payment Confirmation and Reconciliation in Senegal

Wave Business Webhooks: Automating Payment Confirmation and Reconciliation in Senegal

E-commerce

The problem with manual payment verification

Most small Senegalese e-commerce stores check Wave payments by opening the app and scanning the transaction list. That works for five orders a day. It breaks completely at fifty. Wave Business webhooks let your server receive an automatic HTTP notification the moment a payment completes, fails, or expires — no polling, no manual checks, no missed orders.

How Wave webhooks work

When a payment session changes status, Wave sends an HTTP POST to a URL you register in the Wave Business dashboard. The payload is a JSON object describing the event type and the associated payment data.

Sample Wave webhook payload:

`json

{

"id": "evt_01HX9K2M",

"type": "checkout.completed",

"data": {

"id": "chk_01HX9K1A",

"client_reference": "order_1042",

"amount": "50000",

"currency": "XOF",

"when_completed": "2026-06-09T10:15:44Z"

}

}

`

The client_reference field is the value you passed when creating the checkout session — it's the bridge between the Wave event and your database order.

Validating the HMAC-SHA256 signature

Every incoming webhook must be authenticated before processing. Wave signs each request with a Wave-Signature header containing an HMAC-SHA256 of the raw request body using your webhook secret.

Validation in Node.js / TypeScript:

`typescript

import crypto from 'crypto';

function validateWaveWebhook(

rawBody: Buffer,

signatureHeader: string,

secret: string

): boolean {

const expected = crypto

.createHmac('sha256', secret)

.update(rawBody)

.digest('hex');

return crypto.timingSafeEqual(

Buffer.from(expected),

Buffer.from(signatureHeader)

);

}

Need a professional website?

Kolonell builds websites that attract clients, optimized for the Sénégalese market. Free quote in 2 minutes.

`

Use timingSafeEqual to prevent timing attacks. Reject any request where the signature does not match with HTTP 400 — do not process it further.

Idempotency: handling duplicate deliveries

Wave may deliver the same event more than once if your server timed out on a previous attempt. Without idempotency guards, an order could be fulfilled twice. The fix is a processed_events table with a unique constraint on the event ID:

`sql

CREATE TABLE processed_events (

event_id VARCHAR(64) PRIMARY KEY,

processed_at TIMESTAMPTZ DEFAULT NOW()

);

`

Before processing:

`typescript

const result = await db.query(

'INSERT INTO processed_events(event_id) VALUES($1) ON CONFLICT DO NOTHING RETURNING event_id',

[event.id]

);

if (result.rowCount === 0) {

return res.status(200).json({ status: 'already_processed' });

}

`

Handling timeouts and retry logic

Wave marks a webhook as failed if your server does not respond with HTTP 200 within 10 seconds. It then retries with exponential backoff: after 1 minute, 5 minutes, 30 minutes, 2 hours, then stops.

Best practices:

  • Respond HTTP 200 immediately, before finishing processing. Offload the actual work to a queue (BullMQ, a database-backed job table, or even setImmediate for small volumes).
  • Monitor your webhook endpoint with Uptime Robot or Better Uptime. A 2-hour outage means you'll miss Wave's final retry attempt.
  • Log every incoming webhook to a webhook_log table before any processing — this gives you a full audit trail and a source for manual replay.

Building the reconciliation job

Even with perfectly configured webhooks, edge cases exist. Build a reconciliation job that runs every 15–30 minutes:

  • Fetch all orders in "pending" status that are more than 10 minutes old.
  • For each, call GET /v1/checkout/sessions/{session_id} on the Wave API.
  • If the API returns status: "complete", mark the order as paid and trigger fulfillment.
  • If status: "expired", cancel the order and release reserved inventory.

For a store processing 500,000–2,000,000 FCFA per day, this safety net is non-negotiable. Webhooks cover 99% of cases; the reconciliation job covers the other 1%.

FAQ

How long does Wave retry failed webhooks?

Wave retries for approximately 24 hours with exponential backoff. After that, no further retries occur — which is why the polling reconciliation job is essential.

Can I have multiple webhook endpoints for the same API key?

Wave Business supports one endpoint per API key. Use separate API keys (and separate endpoints) for staging and production environments.

Is HTTPS required for receiving Wave webhooks?

Yes. Wave will not deliver webhooks to plain HTTP URLs. Your endpoint must be reachable over HTTPS with a valid certificate — Let's Encrypt works fine.

How do I test webhooks locally before deploying?

Use ngrok or a similar tunnel tool to expose your local server with a public HTTPS URL. Register that temporary URL in the Wave Business dashboard for testing, then switch to your production URL at deploy time.

Let's talk about your project. Kolonell builds Wave-integrated checkout systems with full webhook handling and automated reconciliation. WhatsApp +221 77 596 93 33.

Tags:#webhooks#Wave Business#payment reconciliation#HMAC signature#idempotency#Senegal e-commerce#Node.js
Share:

Mohamed Bah

Fondateur, Kolonell

Passionate about digital and entrepreneurship in Africa, Mohamed has been helping Sénégalese businesses with their digital transformation since 2020. Founder of Kolonell, he believes every SME deserves a professional and accessible online présence.