Engineering

Payment Webhook Patterns: Routing Transactions Across Multiple Services

Payment webhooks are the most critical events in any fintech or e-commerce system. A single payment event — a successful charge, a refund, a chargeback — needs to reach multiple services: your accounting ledger, your CRM, your analytics pipeline, your notification system, and sometimes your fraud detection service. Here's how to build a reliable payment webhook architecture.

The Problem with Direct Integration

When you integrate a payment processor like Stripe, Flutterwave, or Paystack, the simplest approach is to set up a single webhook endpoint that handles the event inline:

app.post('/webhooks/payments', (req, res) => {
  const event = req.body
  await updateAccountingLedger(event)
  await notifyCustomer(event)
  await updateCRM(event)
  await pushToAnalytics(event)
  res.status(200).end()
})

This works until it doesn't. If the CRM is down, the entire webhook processing pipeline blocks — notifications don't send, analytics don't update, and the customer gets charged without any of the downstream systems knowing. One failing service takes down your entire payment workflow.

This is where webhook multiplexing changes the game.

Pattern 1: Fan-Out to Multiple Destinations

Instead of chaining all downstream calls sequentially, a webhook multiplexer fans out the event to each destination independently:

  ┌──────────────────┐     ┌──────────────────┐
  │                  │     │  Accounting      │
  │  Payment         │────►│  Ledger          │
  │  Processor       │     ├──────────────────┤
  │  (Stripe/        │     │  CRM             │
  │   Flutterwave)   │────►│  (HubSpot,       │
  │                  │     │   Salesforce)    │
  │  1 webhook       │     ├──────────────────┤
  │  N destinations  │────►│  Analytics       │
  │                  │     │  (Mixpanel,      │
  │                  │     │   PostHog)       │
  │                  │     ├──────────────────┤
  │                  │────►│  Notifications   │
  │                  │     │  (Email, SMS)    │
  └──────────────────┘     └──────────────────┘

Each destination gets its own queue, retry policy, and delivery log. If the CRM is down, the accounting ledger still gets updated. If analytics is slow, notifications still fire. Failures are isolated, not cascading.

Pattern 2: Event-Type Routing with Filters

Not every payment event should go to every service. A chargeback alert is critical for fraud detection but irrelevant to your CRM pipeline. Filter rules let you route events based on payload content:

Destination: Accounting Ledger
Filter: {"event": "charge.succeeded"}
→ Only successful charges hit the ledger

Destination: Fraud Detection
Filter: {"event": "chargeback.created"}
→ Only chargebacks trigger fraud alerts

Destination: Analytics Pipeline
Filter: {"event": ["charge.succeeded",
                    "charge.refunded",
                    "chargeback.created"]}
→ Analytics gets all payment events

This keeps each destination lean — it only receives the events it actually needs to process, reducing noise and processing overhead.

Pattern 3: Idempotent Payment Processing

Payment processors sometimes retry webhook deliveries. Stripe, for example, may retry a webhook if your endpoint doesn't respond within 3 seconds. Without idempotency, a single payment could be processed multiple times.

The solution is an idempotency key — typically the payment event ID:

POST /p/acme_project
Idempotency-Key: evt_3Oq... (Stripe event ID)
Content-Type: application/json

{
  "event": "charge.succeeded",
  "id": "evt_3Oq...",
  "amount": 5000,
  "currency": "NGN"
}

hooksnode deduplicates inbound webhooks with the same idempotency key, so even if Stripe sends the same event twice, your destinations only receive it once.

Pattern 4: Signature Verification Chain

Payment webhooks carry sensitive data — amounts, customer details, transaction IDs. You need end-to-end authenticity:

  1. Inbound — Verify the payment processor's signature (Stripe uses HMAC-SHA256 with a webhook secret)
  2. Outbound — Sign the forwarded request so each destination can verify it came from your webhook infrastructure

hooksnode supports both sides: you validate the inbound signature in your application, and when you enable signing on a destination, hooksnode signs every request forwarded to it with an X-Hooksnode-Signature: sha256=... header. Your downstream services verify this header before processing.

Pattern 5: Payment Reconciliation via Payload Transformation

Different downstream systems need different data shapes. Your accounting system might need flat key-value pairs, while your CRM expects nested objects. Payload transformation lets you reshape the outbound event per destination without changing the original webhook:

// Accounting destination gets flat format
{"transaction_id": "{{.id}}", "amount": {{.amount}}, "currency": "{{.currency}}"}

// CRM destination gets wrapped envelope
{"source": "payment_processor", "event": "charge.succeeded", "data": {{.}}}

// Notification service gets simplified payload
{"customer_email": "{{.billing_details.email}}", "paid_amount": {{.amount}}}

Real-World Example: Multi-Product Payment Routing

A transportation platform like Shuttlers receives payment webhooks from Flutterwave after every ride transaction. The webhook needs to reach:

  • The ride service — to mark the trip as paid and release the driver payment
  • The accounting system — to record revenue per route
  • The customer wallet — to credit loyalty points
  • The analytics pipeline — to track payment success rates per route
  • The notification service — to send the receipt

With direct integration, maintaining this across all products becomes a cross-cutting concern that every team implements differently. With a webhook multiplexer, the payment processor sends to one URL, and hooksnode fans out to each internal service with the right filters, transformations, and retry policies — ensuring every team gets the data they need, reliably.

Key Takeaways

  • Fan-out delivery isolates failures — one downstream service going down doesn't block others
  • Filter rules keep each destination lean by routing only relevant events
  • Idempotency prevents duplicate payment processing
  • End-to-end signature verification ensures authenticity from processor to destination
  • Payload transformation lets each service receive the data format it needs

hooksnode was built for exactly these patterns. One inbound URL, automatic fan-out, built-in retries with exponential backoff, per-destination filters and transformations, and a live event inspector — so you always know what's happening with your payment webhooks.

Try hooksnode free

10,000 events per month, free forever. No credit card required.

Start free →