Skip to content

External Webhooks Guide ​

External webhooks allow you to send payment events to your own services, Discord, Slack, analytics platforms, or any HTTP endpoint in real-time.

Overview ​

External webhooks are perfect for:

  • Notifications: Send alerts to Discord or Slack
  • Analytics: Track payment events in your analytics platform
  • Business Logic: Trigger custom workflows in your application
  • Monitoring: Monitor payment failures and subscription cancellations
  • Integrations: Connect to third-party services

Quick Start ​

1. Enable External Webhooks ​

Add to your environment configuration in Pubflow dashboard:

bash
# Enable the system
EXTERNAL_WEBHOOKS_ENABLED=true

# Optional: Enable debug logs
WEBHOOK_DEBUG_MODE=true

2. Configure Your First Webhook ​

bash
# Discord notifications for failed payments
WEBHOOK_1_NAME=discord_alerts
WEBHOOK_1_URL=https://discord.com/api/webhooks/123456789/your-webhook-token
WEBHOOK_1_EVENTS=payment.failed,subscription.cancelled

3. Save Configuration ​

Click Save Configuration in Pubflow dashboard. The system will automatically detect and load your webhooks.

Configuration Reference ​

Basic Webhook Configuration ​

Each webhook uses numbered environment variables:

bash
WEBHOOK_1_NAME=my_webhook           # Required: Friendly name
WEBHOOK_1_URL=https://example.com   # Required: Endpoint URL
WEBHOOK_1_EVENTS=payment.success    # Required: Which events to send

Complete Configuration Options ​

bash
WEBHOOK_1_NAME=analytics
WEBHOOK_1_URL=https://analytics.company.com/webhooks
WEBHOOK_1_SECRET=your_secret_key                    # Optional: For signature validation
WEBHOOK_1_EVENTS=payment.*,subscription.*           # Required: Event patterns
WEBHOOK_1_HEADERS={"Authorization":"Bearer token"}  # Optional: Custom headers (JSON)
WEBHOOK_1_TIMEOUT=30                                # Optional: Timeout in seconds
WEBHOOK_1_RETRIES=3                                 # Optional: Retry attempts

Multiple Webhooks ​

Add as many webhooks as you need by incrementing the number:

bash
# Discord for alerts
WEBHOOK_1_NAME=discord
WEBHOOK_1_URL=https://discord.com/api/webhooks/123/abc
WEBHOOK_1_EVENTS=payment.failed

# Slack for notifications
WEBHOOK_2_NAME=slack
WEBHOOK_2_URL=https://hooks.slack.com/services/T00/B00/XXX
WEBHOOK_2_EVENTS=payment.success,subscription.created

# Your internal API
WEBHOOK_3_NAME=internal_api
WEBHOOK_3_URL=https://api.yourcompany.com/webhooks/payments
WEBHOOK_3_SECRET=your_api_secret
WEBHOOK_3_EVENTS=*
WEBHOOK_3_HEADERS={"Authorization":"Bearer your-token"}

# Analytics platform
WEBHOOK_4_NAME=analytics
WEBHOOK_4_URL=https://analytics.platform.com/events
WEBHOOK_4_EVENTS=payment.*,customer.*

Event Types ​

Payment Events ​

  • payment_intent.succeeded - Successful payment
  • payment_intent.payment_failed - Failed payment
  • payment_intent.canceled - Canceled payment
  • charge.refund.created - Refund processed

Subscription Events ​

  • customer.subscription.created - New subscription
  • customer.subscription.updated - Subscription modified
  • customer.subscription.deleted - Subscription canceled
  • invoice.payment_failed - Subscription payment failed

Customer Events ​

  • customer.created - New customer
  • customer.updated - Customer information changed
  • customer.deleted - Customer removed

Event Patterns ​

Use patterns to match multiple events:

bash
# All payment events
WEBHOOK_1_EVENTS=payment.*

# All subscription events
WEBHOOK_2_EVENTS=subscription.*

# All customer events
WEBHOOK_3_EVENTS=customer.*

# Everything
WEBHOOK_4_EVENTS=*

# Specific events only
WEBHOOK_5_EVENTS=payment_intent.succeeded,customer.subscription.created

Webhook Payload ​

Payload Structure ​

json
{
  "event": "payment_intent.succeeded",
  "data": {
    "webhook_id": "wh_stripe_123456",
    "event_data": {
      "id": "evt_stripe_789",
      "type": "payment_intent.succeeded",
      "data": {
        "object": {
          "id": "pi_payment_123",
          "amount": 5000,
          "currency": "usd",
          "status": "succeeded"
        }
      }
    },
    "payment_id": "pi_payment_123",
    "customer_id": "cus_customer_456",
    "timestamp": "2024-01-15T10:30:00Z",
    "payment": {
      "id": "pay_bridge_123",
      "amount_cents": 5000,
      "currency": "USD",
      "status": "succeeded",
      "concept": "Subscription Payment"
    },
    "customer": {
      "id": "cus_bridge_456",
      "email": "john.doe@example.com",
      "name": "John Doe"
    }
  }
}

Integration Examples ​

Discord Notifications ​

bash
WEBHOOK_1_NAME=discord_payments
WEBHOOK_1_URL=https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN
WEBHOOK_1_EVENTS=payment.failed,subscription.cancelled

Discord Webhook Handler:

typescript
// Your Discord webhook receives:
{
  "content": "💳 Payment Event: payment_intent.succeeded",
  "embeds": [{
    "title": "Payment Successful",
    "description": "Payment of $50.00 completed",
    "color": 3066993,
    "fields": [
      { "name": "Customer", "value": "john.doe@example.com" },
      { "name": "Amount", "value": "$50.00" },
      { "name": "Status", "value": "succeeded" }
    ]
  }]
}

Slack Notifications ​

bash
WEBHOOK_2_NAME=slack_payments
WEBHOOK_2_URL=https://hooks.slack.com/services/YOUR/WEBHOOK/URL
WEBHOOK_2_EVENTS=payment.success,subscription.created

Custom API Endpoint ​

bash
WEBHOOK_3_NAME=internal_api
WEBHOOK_3_URL=https://api.yourcompany.com/webhooks/payments
WEBHOOK_3_SECRET=your_webhook_secret
WEBHOOK_3_EVENTS=*
WEBHOOK_3_HEADERS={"Authorization":"Bearer your-api-token","X-Custom-Header":"value"}

API Handler Example (Node.js/Express):

typescript
import express from 'express';
import crypto from 'crypto';

const app = express();

app.post('/webhooks/payments', express.json(), (req, res) => {
  // 1. Verify signature (if secret is configured)
  const signature = req.headers['x-webhook-signature'];
  const secret = process.env.WEBHOOK_SECRET;
  
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(req.body))
    .digest('hex');
  
  if (signature !== expectedSignature) {
    return res.status(401).send('Invalid signature');
  }
  
  // 2. Process event
  const { event, data } = req.body;
  
  switch (event) {
    case 'payment_intent.succeeded':
      console.log('Payment succeeded:', data.payment);
      // Update your database, send email, etc.
      break;
      
    case 'payment_intent.payment_failed':
      console.log('Payment failed:', data.payment);
      // Send notification, retry logic, etc.
      break;
      
    case 'customer.subscription.created':
      console.log('New subscription:', data.subscription);
      // Grant access, send welcome email, etc.
      break;
  }
  
  // 3. Respond with 200 OK
  res.status(200).send('OK');
});

Security ​

Signature Validation ​

When you configure a WEBHOOK_X_SECRET, Bridge Payments signs each webhook request:

bash
WEBHOOK_1_SECRET=your_secret_key

Verify signature in your endpoint:

typescript
import crypto from 'crypto';

function verifyWebhookSignature(payload: string, signature: string, secret: string): boolean {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  
  return signature === expectedSignature;
}

// In your handler
const signature = req.headers['x-webhook-signature'];
const isValid = verifyWebhookSignature(
  JSON.stringify(req.body),
  signature,
  process.env.WEBHOOK_SECRET
);

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

Best Practices ​

  1. Always verify signatures when using secrets
  2. Use HTTPS for webhook URLs
  3. Respond quickly (within 5 seconds) to avoid timeouts
  4. Process asynchronously for long-running tasks
  5. Handle retries gracefully (check for duplicate events)
  6. Log all webhook events for debugging

Retry Logic ​

Bridge Payments automatically retries failed webhook deliveries:

  • Default retries: 3 attempts
  • Retry delays: Exponential backoff (1s, 2s, 4s)
  • Timeout: 30 seconds per attempt (configurable)

Configure retry behavior:

bash
WEBHOOK_1_RETRIES=5        # Number of retry attempts
WEBHOOK_1_TIMEOUT=60       # Timeout in seconds

Debugging ​

Enable debug mode to see webhook delivery logs:

bash
WEBHOOK_DEBUG_MODE=true

Logs will show:

  • Webhook configuration loaded
  • Event matching
  • HTTP requests sent
  • Response status codes
  • Retry attempts

Next Steps ​