Skip to content

Webhook Integration

Webhooks notify your application when events occur in OVN Pay.

Webhook Events

EventDescription
payout.createdA payout was created
payout.succeededA payout completed successfully
payout.failedA payout failed
payout.cancelledA payout was cancelled
batch.releasedA batch was released for processing
batch.completedA batch finished processing

Setup Webhook URL

Configure your webhook URL via API:

http
POST /api/v1/webhooks

Body:

json
{
  "url": "https://your-app.com/webhooks/ovn",
  "events": ["payout.succeeded", "payout.failed"]
}

Verify Webhook Signatures

All webhook payloads are signed with a signature header:

X-Webhook-Signature: t=1234567890,v1=abc123...

Verification (TypeScript)

typescript
import { createHmac } from 'crypto';

export function verifyWebhookSignature(
  payload: string,
  signature: string,
  secret: string
): boolean {
  const [t, v1] = signature.split(',');

  const expectedSignature = createHmac('sha256', secret)
    .update(`${t}.${payload}`)
    .digest('hex');

  return `v1=${expectedSignature}` === v1;
}

Verification (Node.js Express)

javascript
import crypto from 'crypto';

app.post('/webhooks/ovn', (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const payload = JSON.stringify(req.body);

  const hmac = crypto.createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(payload)
    .digest('hex');

  const expected = `v1=${hmac}`;

  if (signature !== expected) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Process webhook
  const event = req.body;
  console.log('Event type:', event.type);

  res.json({ success: true });
});

Verification (Go)

go
import (
  "crypto/hmac"
  "encoding/hex"
  "strings"
)

func verifyWebhookSignature(payload, signature, secret string) bool {
    parts := strings.Split(signature, ",")
    t := parts[0]
    v1 := parts[1]

    h := hmac.NewSHA256.New()
    h.Write([]byte(t + "." + payload))
    expected := "v1=" + hex.EncodeToString(h.Sum(nil))

    return v1 == expected
}

Best Practices

  1. Use HTTPS - Always use HTTPS for webhook URLs
  2. Verify Signatures - Always verify webhook signatures
  3. Handle Idempotency - Webhooks may be sent multiple times
  4. Respond Quickly - Respond within 5 seconds with 200 OK
  5. Retry Logic - Implement exponential backoff for failed deliveries
  6. Log Everything - Log all webhook events for debugging

Event Payload Structure

All webhook events follow this structure:

json
{
  "id": "evt_abc123",
  "type": "payout.succeeded",
  "data": {
    "payoutId": "payout_123",
    "driverId": "drv_abc123",
    "amount": 50000,
    "status": "succeeded",
    "completedAt": "2025-01-15T10:30:00Z"
  },
  "timestamp": "2025-01-15T10:30:00Z"
}