HivePay Docs v1.0
Sign In Get API Keys
HMAC-SHA256 Instant Event Notification

Webhooks & Signature Verification

HivePay sends real-time HTTP POST notifications to your server whenever an important event happens (e.g. when a customer approves a USSD push deposit or when a payout completes).

Event Types

Event Trigger Condition
transaction.success Customer approved collection prompt or a payout was delivered successfully.
transaction.failed Customer entered incorrect PIN, prompt timed out, or payout was rejected by carrier.

Signature Header: X-HivePay-Signature

Every webhook request dispatched by HivePay includes an X-HivePay-Signature header with two key components:

X-HivePay-Signature: t=1726245000,v=3a7b9c48f2e18d6...
  • t: Unix epoch timestamp when the webhook was sent. Used to reject replay attacks older than 5 minutes.
  • v: The hexadecimal HMAC-SHA256 signature calculated from t + "." + rawBody using your secret key.

Verification Code Examples

Select your language below to view a complete, production-ready webhook signature verification handler:

<?php

function verifyHivePayWebhook($payloadString, $signatureHeader, $webhookSecret) {
    // 1. Parse t and v from header: t=1726245000,v=3a7b9c...
    $parts = explode(',', $signatureHeader);
    $timestamp = explode('=', $parts[0] ?? '')[1] ?? '';
    $receivedSig = explode('=', $parts[1] ?? '')[1] ?? '';

    // 2. Protect against replay attacks (ignore requests older than 5 mins)
    if (abs(time() - (int)$timestamp) > 300) {
        return false;
    }

    // 3. Compute expected HMAC-SHA256 signature
    $signedPayload = $timestamp . '.' . $payloadString;
    $expectedSig = hash_hmac('sha256', $signedPayload, $webhookSecret);

    // 4. Secure constant-time string comparison
    return hash_equals($expectedSig, $receivedSig);
}

// Controller entry point:
$rawPayload = file_get_contents('php://input');
$signatureHeader = $_SERVER['HTTP_X_HIVEPAY_SIGNATURE'] ?? '';
$secret = getenv('HIVEPAY_WEBHOOK_SECRET');

if (verifyHivePayWebhook($rawPayload, $signatureHeader, $secret)) {
    $event = json_decode($rawPayload, true);

    if ($event['status'] === 'success') {
        // Mark customer order as paid
        $orderId = $event['reference'];
    }

    http_response_code(200);
    echo json_encode(['received' => true]);
} else {
    http_response_code(400);
    echo 'Invalid signature';
}