DocsWebhooks

Webhooks

Receive real-time notifications when payment events occur. Webhooks allow you to automate order fulfillment and keep your system in sync.

How Webhooks Work

When a payment event occurs, PayHub sends an HTTP POST request to your configured webhook URL with details about the event. Your server should respond with a 2xx status code to acknowledge receipt.

Webhook Setup

Configure your webhook endpoints in the Dashboard → Settings → Webhooks. You can set up multiple endpoints for different environments.

Event Types

Subscribe to the events relevant to your integration:

Event TypeDescription
payment.createdPayment was created
payment.detectedTransaction detected on blockchain
payment.confirmingTransaction is being confirmed
payment.confirmedRequired confirmations reached
payment.completedPayment fully processed
payment.expiredPayment expired without receiving funds
payment.underpaidReceived less than expected amount
payment.overpaidReceived more than expected amount

Payload Structure

All webhook payloads follow the same structure:

JSON
{
  "id": "evt_abc123xyz",
  "type": "payment.confirmed",
  "createdAt": "2024-01-15T12:00:00Z",
  "data": {
    "id": "pay_abc123xyz",
    "status": "confirmed",
    "amount": "100.00",
    "currency": "USDC",
    "network": "polygon",
    "address": "0x1234...abcd",
    "txHash": "0xabcd...1234",
    "confirmations": 12,
    "metadata": {
      "orderId": "12345"
    }
  }
}

Signature Verification

All webhooks include a signature in the x-payhub-signature header. Always verify this signature to ensure the webhook is genuinely from PayHub.

Security

Never process webhooks without verifying the signature first. This prevents attackers from sending fake webhook events to your endpoint.

Using the SDK

webhook-handler.ts
import { PayHubClient } from '@payhub/sdk';
import crypto from 'crypto';

const client = new PayHubClient({ apiKey: 'your-api-key' });

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

  // Verify signature
  const isValid = client.webhooks.verifySignature({
    payload: req.body,
    signature,
    timestamp,
    secret: process.env.WEBHOOK_SECRET,
  });

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  // Process the webhook
  const event = JSON.parse(req.body);
  handleWebhook(event);

  res.status(200).send('OK');
});

Manual Verification

JavaScript
// Manual signature verification
const crypto = require('crypto');

function verifySignature(payload, signature, timestamp, secret) {
  const signedPayload = `${timestamp}.${payload}`;
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

Retry Policy

If your endpoint returns a non-2xx status code or times out (30 seconds), we'll retry with exponential backoff:

Retry 1
10 seconds
Retry 2
30 seconds
Retry 3
1 minute
Retry 4
5 minutes
Retry 5
10 minutes
Retry 6
30 minutes
Retry 7
1 hour
Retry 8
2 hours
Retry 9
4 hours
Retry 10
8 hours

After 10 failed attempts, the webhook is moved to the dead letter queue. You can manually replay failed webhooks from the Dashboard.

Best Practices

Return 200 quickly

Acknowledge the webhook immediately, then process asynchronously. Don't make the response wait for your business logic.

Handle duplicates

Use the event ID to deduplicate. The same event might be delivered multiple times in rare cases.

Verify before trusting

After receiving a webhook, call the API to verify the payment status before fulfilling orders.

Log everything

Log all incoming webhooks for debugging and audit purposes.