Webhooks

Receive real-time notifications about payment events with secure, reliable webhook delivery and automatic retries.

Events Overview

VeriPay sends webhook events to notify your application when important events happen in your account. Webhooks are particularly useful for events that are not triggered by direct API requests.

Payment Events

  • • payment.validated
  • • payment.rejected
  • • payment.proof_uploaded
  • • payment_link.created

Invoice Events

  • • invoice.paid
  • • invoice.overdue
  • • invoice.viewed
  • • invoice.sent

Security & Verification

VeriPay signs all webhook payloads with HMAC-SHA256 using your webhook signing secret. Always verify the signature before processing webhook data.

Signature Verification

<?php
// Get the signature from headers
$signature = $_SERVER['HTTP_X_VERIPAY_SIGNATURE'];
$payload = file_get_contents('php://input');

// Parse the signature header
list($timestamp, $signature_hash) = explode(',', $signature);
$timestamp = str_replace('t=', '', $timestamp);
$signature_hash = str_replace('v1=', '', $signature_hash);

// Verify timestamp (prevent replay attacks)
if (abs(time() - (int)$timestamp) > 300) { // 5 minutes
    http_response_code(400);
    exit('Request too old');
}

// Compute expected signature
$expected_signature = hash_hmac(
    'sha256',
    $timestamp . '.' . $payload,
    $webhook_secret
);

// Verify signature (constant time)
if (!hash_equals($expected_signature, $signature_hash)) {
    http_response_code(401);
    exit('Invalid signature');
}

// Process the webhook
$event = json_decode($payload, true);
processWebhook($event);

Payload Format

All webhook events follow a consistent JSON structure:

{
  "id": "evt_1234567890abcdef",
  "type": "payment.validated",
  "occurred_at": "2024-08-14T10:30:00Z",
  "data": {
    "payment_link": {
      "id": "ABC123XY",
      "status": "verified",
      "amount": 150.00,
      "currency": "TTD",
      "reference": "ORDER-12345",
      "source_reference": "order_12345"
    }
  },
  "meta": {
    "attempt": 1
  }
}

Retries & Backoff

VeriPay automatically retries failed webhook deliveries with exponential backoff:

Attempt Delay Total Time
1 30 seconds 30s
2 2 minutes 2m 30s
3 10 minutes 12m 30s
4 30 minutes 42m 30s
5 2 hours 2h 42m 30s
6 6 hours 8h 42m 30s

Sample Webhook Endpoint

<?php
// routes/web.php
Route::post('/webhooks/veripay', [WebhookController::class, 'handle']);

// app/Http/Controllers/WebhookController.php
class WebhookController extends Controller
{
    public function handle(Request $request)
    {
        // Verify the signature (see security section)
        $this->verifySignature($request);

        $event = $request->all();

        switch ($event['type'] ?? null) {
            case 'payment.validated':
                $this->handlePaymentValidated($event['data']);
                break;

            case 'payment.rejected':
                $this->handlePaymentRejected($event['data']);
                break;

            case 'invoice.paid':
                $this->handleInvoicePaid($event['data']);
                break;

            default:
                Log::info('Unhandled webhook event: ' . ($event['type'] ?? 'unknown'));
        }

        return response()->json(['status' => 'success']);
    }

    private function handlePaymentValidated(array $data): void
    {
        $paymentLink = $data['payment_link'];

        // Update your local order status
        Order::where('veripay_invoice_id', $paymentLink['id'])
            ->update(['status' => 'paid']);

        // Optional: Send confirmation email, etc.
    }
}

Webhook Configuration

Configure your webhook endpoints in the VeriPay dashboard:

  1. Navigate to Settings → Webhooks in your dashboard
  2. Add your webhook endpoint URL
  3. Select the events you want to receive
  4. Copy your webhook signing secret
  5. Test the endpoint with a sample event

Next Steps

Now that you understand webhooks, implement them in your application: