Documentation

Paystack Webhook Dispatcher Guide

This service acts as a "Traffic Controller" for Paystack webhooks. Since Paystack only allows one global Webhook URL, this dispatcher receives all events and routes them to the correct application based on the metadata.app_name field.


Setup

1. Environment Variables

Add the following to your environment variables (Vercel or .env.local):

# The secret key from your Paystack Dashboard (Settings > API Keys & Webhooks)PAYSTACK_SECRET=sk_live_xxxxxxx# Routing Table: Map metadata.app_name to destination URLs # Pattern: PAYSTACK_FORWARD_URL_[APP_NAME]=[URL] # The app name in the environment variable should be uppercase and non-alphanumeric characters replaced with underscores. # Example mappings:PAYSTACK_FORWARD_URL_SAAS_APP=https://saas.f8ware.com/api/webhooksPAYSTACK_FORWARD_URL_ECOMMERCE=https://shop.f8ware.com/api/webhooks

2. Paystack Configuration

  1. Log in to your Paystack Dashboard.
  2. Go to Settings > API Keys & Webhooks.
  3. Set the Webhook URL to https://api.f8ware.com/api/webhooks/paystack.

3. Usage in Applications

When initiating a transaction (e.g., via Paystack Inline or API), ensure you include the app_name in the metadata:

const response = await paystack.transaction.initialize({
  email: "customer@example.com",
  amount: 10000,
  metadata: {
    app_name: "saas_app" // This must match the ENV variable suffix
  }
});

Adding a New Application

When you launch a new application (e.g., store.f8ware.com) and want it to receive Paystack webhooks through the central dispatcher, follow this two-step process:

Step 1: Update Dispatcher Environment Variables

On the central dispatcher server (api.f8ware.com), add a new environment variable pointing to your new application. If we decide to use "store_app" as the identifier:

PAYSTACK_FORWARD_URL_STORE_APP=https://store.f8ware.com/api/webhooks

The dispatcher automatically converts your identifier to uppercase and replaces non-alphanumeric characters with underscores to look up this key.

Step 2: Update Client Application Metadata

On your new store application (store.f8ware.com), include the matching app_name inside the transaction metadata when initializing payments:

const response = await paystack.transaction.initialize({
  email: "customer@store.com",
  amount: 5000,
  metadata: {
    app_name: "store_app", // <--- The crucial part
    cart_id: "12345" // Your standard metadata
  }
});

Security

  • Signature Verification: The dispatcher verifies every request using HMAC SHA512 and your PAYSTACK_SECRET.
  • Payload Integrity: The raw request body is used for verification to ensure no tampering.
  • Forwarding: The dispatcher forwards the original x-paystack-signature header so downstream applications can also verify the request if they choose.

Error Handling

401 Unauthorized

Invalid or missing signature.

400 Bad Request

Missing metadata.app_name or malformed JSON.

404 Not Found

No destination URL configured for the provided app_name.

502 Bad Gateway

Forwarding to the destination URL failed.


Testing

You can run the internal tests to verify the logic:

npx tsx tests/webhook.test.ts