PayGate

Webhooks

Create and manage webhook endpoints.

Webhooks API

The Webhooks API allows you to create, manage, and test webhook endpoints that receive event notifications from PayGate.

The Webhook Endpoint Object

{
  "id": "we_abc123def456",
  "object": "webhook_endpoint",
  "url": "https://yoursite.com/webhooks",
  "description": "Production webhook",
  "status": "enabled",
  "events": [
    "payment.succeeded",
    "payment.failed",
    "subscription.created"
  ],
  "secret": "whsec_...",
  "created_at": "2024-01-15T10:00:00Z"
}

Attributes

AttributeTypeDescription
idstringUnique identifier
objectstringAlways "webhook_endpoint"
urlstringThe endpoint URL
descriptionstringDescription
statusstringenabled or disabled
eventsarrayEvents to subscribe to
secretstringSigning secret (only shown once)
created_atstringCreation timestamp

Create a Webhook Endpoint

Creates a new webhook endpoint.

POST /v1/webhooks

Request Body

ParameterTypeRequiredDescription
urlstringYesEndpoint URL (must be HTTPS)
eventsarrayYesEvents to subscribe to
descriptionstringNoDescription
curl -X POST https://api.44.200.142.19.nip.io/v1/webhooks \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yoursite.com/webhooks",
    "events": [
      "payment.succeeded",
      "payment.failed",
      "subscription.created",
      "subscription.canceled"
    ],
    "description": "Production webhook"
  }'
const endpoint = await paygate.webhooks.endpoints.create({
  url: 'https://yoursite.com/webhooks',
  events: [
    'payment.succeeded',
    'payment.failed',
    'subscription.created',
    'subscription.canceled'
  ],
  description: 'Production webhook'
})

// Save the secret! It's only shown once
console.log(endpoint.secret) // whsec_...
endpoint = client.webhooks.endpoints.create(
    url='https://yoursite.com/webhooks',
    events=[
        'payment.succeeded',
        'payment.failed',
        'subscription.created',
        'subscription.canceled'
    ],
    description='Production webhook'
)

# Save the secret! It's only shown once
print(endpoint.secret)  # whsec_...
$endpoint = $paygate->webhooks->endpoints->create([
    'url' => 'https://yoursite.com/webhooks',
    'events' => [
        'payment.succeeded',
        'payment.failed',
        'subscription.created',
        'subscription.canceled'
    ],
    'description' => 'Production webhook'
]);

// Save the secret! It's only shown once
echo $endpoint->secret; // whsec_...

Save the webhook secret immediately. It's only shown once when the endpoint is created.


Retrieve a Webhook Endpoint

Retrieves a webhook endpoint by ID.

GET /v1/webhooks/:id
curl https://api.44.200.142.19.nip.io/v1/webhooks/we_abc123def456 \
  -H "Authorization: Bearer sk_test_..."
const endpoint = await paygate.webhooks.endpoints.retrieve('we_abc123def456')
endpoint = client.webhooks.endpoints.retrieve('we_abc123def456')

Update a Webhook Endpoint

Updates a webhook endpoint.

PATCH /v1/webhooks/:id

Request Body

ParameterTypeDescription
urlstringEndpoint URL
eventsarrayEvents to subscribe to
descriptionstringDescription
statusstringenabled or disabled
curl -X PATCH https://api.44.200.142.19.nip.io/v1/webhooks/we_abc123def456 \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["payment.succeeded", "payment.failed"],
    "status": "enabled"
  }'
const endpoint = await paygate.webhooks.endpoints.update('we_abc123def456', {
  events: ['payment.succeeded', 'payment.failed'],
  status: 'enabled'
})
endpoint = client.webhooks.endpoints.update('we_abc123def456',
    events=['payment.succeeded', 'payment.failed'],
    status='enabled'
)

Delete a Webhook Endpoint

Deletes a webhook endpoint.

DELETE /v1/webhooks/:id
curl -X DELETE https://api.44.200.142.19.nip.io/v1/webhooks/we_abc123def456 \
  -H "Authorization: Bearer sk_test_..."
await paygate.webhooks.endpoints.delete('we_abc123def456')
client.webhooks.endpoints.delete('we_abc123def456')

List Webhook Endpoints

Returns a list of webhook endpoints.

GET /v1/webhooks
curl https://api.44.200.142.19.nip.io/v1/webhooks \
  -H "Authorization: Bearer sk_test_..."
const endpoints = await paygate.webhooks.endpoints.list()
endpoints = client.webhooks.endpoints.list()

Available Events

Payment Events

EventDescription
payment.createdA payment was created
payment.pendingPayment is pending authorization
payment.processingPayment is being processed
payment.succeededPayment completed successfully
payment.failedPayment failed
payment.canceledPayment was canceled

Subscription Events

EventDescription
subscription.createdSubscription was created
subscription.updatedSubscription was updated
subscription.canceledSubscription was canceled
subscription.pausedSubscription was paused
subscription.resumedSubscription was resumed
subscription.trial_will_endTrial ending in 3 days
subscription.payment_succeededRecurring payment succeeded
subscription.payment_failedRecurring payment failed

Payout Events

EventDescription
payout.createdPayout was created
payout.processingPayout is being processed
payout.completedPayout was successful
payout.failedPayout failed
payout.canceledPayout was canceled

Refund Events

EventDescription
refund.createdRefund was created
refund.completedRefund was successful
refund.failedRefund failed

Checkout Events

EventDescription
checkout.session.completedCheckout session completed
checkout.session.expiredCheckout session expired

Webhook Event Object

{
  "id": "evt_abc123def456",
  "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",
      ...
    }
  }
}

Verifying Signatures

Always verify webhook signatures to ensure requests came from PayGate:

import { Webhook } from '@paygate/node'

const endpointSecret = 'whsec_...'

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

  try {
    const event = Webhook.constructEvent(
      req.body,
      signature,
      endpointSecret
    )

    // Handle the event
    console.log(`Received: ${event.type}`)

    res.json({ received: true })
  } catch (err) {
    console.error('Signature verification failed:', err.message)
    res.status(400).send('Invalid signature')
  }
})
import paygate

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 paygate.SignatureVerificationError as e:
        return 'Invalid signature', 400

    print(f'Received: {event["type"]}')
    return {'received': True}
$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);
    exit('Invalid signature');
}

echo "Received: " . $event->type;
http_response_code(200);

Never skip signature verification in production. Unsigned webhooks could be spoofed.