PayGate

Webhooks

Receive real-time notifications for payment events.

Handle Webhooks

Webhooks allow you to receive real-time notifications when events happen in your PayGate account, such as when a payment succeeds or fails.

How Webhooks Work

  1. A customer makes a payment
  2. PayGate sends a POST request to your webhook endpoint
  3. Your server processes the event and responds with a 200 status
  4. If we don't receive a 200, we'll retry the webhook

Webhooks are signed with your webhook secret, allowing you to verify they came from PayGate.

Set Up Webhooks

Create a Webhook Endpoint

First, create an endpoint on your server to receive webhooks:

import express from 'express'
import { Webhook } from '@paygate/node'

const app = express()
const endpointSecret = process.env.PAYGATE_WEBHOOK_SECRET!

app.post('/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-paygate-signature'] as string

  let event

  try {
    event = Webhook.constructEvent(req.body, signature, endpointSecret)
  } catch (err) {
    console.error('Webhook signature verification failed:', err.message)
    return res.status(400).send(`Webhook Error: ${err.message}`)
  }

  // Handle the event
  switch (event.type) {
    case 'payment.succeeded':
      const payment = event.data.object
      console.log(`Payment ${payment.id} succeeded!`)
      // Update your database, send confirmation email, etc.
      break

    case 'payment.failed':
      console.log('Payment failed:', event.data.object.id)
      // Handle failed payment
      break

    case 'subscription.created':
      console.log('Subscription created')
      break

    default:
      console.log(`Unhandled event type: ${event.type}`)
  }

  res.json({ received: true })
})

app.listen(3000)
from flask import Flask, request
import paygate

app = Flask(__name__)
endpoint_secret = 'whsec_...'

@app.route('/webhooks', methods=['POST'])
def webhook():
    payload = request.data
    signature = request.headers.get('X-PayGate-Signature')

    try:
        event = paygate.Webhook.construct_event(
            payload, signature, endpoint_secret
        )
    except ValueError as e:
        return 'Invalid payload', 400
    except paygate.SignatureVerificationError as e:
        return 'Invalid signature', 400

    # Handle the event
    if event['type'] == 'payment.succeeded':
        payment = event['data']['object']
        print(f"Payment {payment['id']} succeeded!")
    elif event['type'] == 'payment.failed':
        print('Payment failed')

    return {'received': True}

if __name__ == '__main__':
    app.run(port=3000)
<?php
require 'vendor/autoload.php';

$endpointSecret = 'whsec_...';
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_PAYGATE_SIGNATURE'];

try {
    $event = \PayGate\Webhook::constructEvent(
        $payload,
        $signature,
        $endpointSecret
    );
} catch (\Exception $e) {
    http_response_code(400);
    echo 'Webhook Error: ' . $e->getMessage();
    exit();
}

// Handle the event
switch ($event->type) {
    case 'payment.succeeded':
        $payment = $event->data->object;
        error_log("Payment {$payment->id} succeeded!");
        break;
    case 'payment.failed':
        error_log('Payment failed');
        break;
}

http_response_code(200);
echo json_encode(['received' => true]);

Register Your Endpoint

Add your webhook endpoint in the Dashboard:

  1. Go to Settings > Webhooks
  2. Click Add endpoint
  3. Enter your endpoint URL (e.g., https://yoursite.com/webhooks)
  4. Select the events you want to receive
  5. Save and copy your webhook secret

Test Your Webhook

Use the Dashboard to send test events:

  1. Go to your webhook endpoint settings
  2. Click Send test webhook
  3. Select an event type
  4. Check your server logs to verify it was received

Webhook Events

Payment Events

EventDescription
payment.createdA payment was created
payment.processingPayment is being processed
payment.succeededPayment completed successfully
payment.failedPayment failed
payment.cancelledPayment was cancelled

Subscription Events

EventDescription
subscription.createdA subscription was created
subscription.updatedSubscription was updated
subscription.cancelledSubscription was cancelled
subscription.payment_succeededRecurring payment succeeded
subscription.payment_failedRecurring payment failed

Payout Events

EventDescription
payout.createdA payout was initiated
payout.succeededPayout was successful
payout.failedPayout failed

Event Object

{
  "id": "evt_abc123",
  "object": "event",
  "type": "payment.succeeded",
  "created_at": "2024-01-15T10:30:00Z",
  "data": {
    "object": {
      "id": "pay_xyz789",
      "object": "payment",
      "amount": 5000,
      "currency": "GHS",
      "status": "succeeded",
      ...
    }
  }
}

Best Practices

1. Verify Signatures

Always verify webhook signatures to ensure the request came from PayGate:

const event = Webhook.constructEvent(payload, signature, endpointSecret)

2. Return 200 Quickly

Return a 200 response immediately, then process the event asynchronously:

app.post('/webhooks', async (req, res) => {
  // Verify and acknowledge
  const event = Webhook.constructEvent(req.body, signature, secret)
  res.json({ received: true })

  // Process async (use a queue in production)
  processEventAsync(event)
})

3. Handle Duplicate Events

Use the event ID to handle duplicate deliveries:

if (await isEventProcessed(event.id)) {
  return res.json({ received: true })
}

await processEvent(event)
await markEventProcessed(event.id)

4. Use HTTPS

Always use HTTPS endpoints in production. We won't deliver webhooks to HTTP endpoints in live mode.

Retry Policy

If your endpoint doesn't return a 2xx status code, we'll retry with exponential backoff:

AttemptDelay
1Immediate
25 minutes
330 minutes
42 hours
58 hours
624 hours

After 6 failed attempts, the webhook is marked as failed and we'll send you an email notification.