Skip to content
Return to Notes/architecting-idempotent-payment-webhooks
Dev Notes 2025-05-20 8 MIN READ

Architecting Idempotent Payment Webhooks in High-Latency Networks

When network jitter causes mobile networks to retry billing notifications 4 times within 800ms, standard DB locks fail under load. Here is how we implemented distributed Redis token buckets to achieve zero duplicate charges across West African payment rails.

Victor Okeke
Victor Okeke
Lead Full-Stack Engineer // Abuja, NG
NOTE REF:#014

Architecting Idempotent Payment Webhooks in High-Latency Networks

When network jitter causes mobile telecommunications carriers in emerging markets to retry billing notifications multiple times in sub-second intervals, standard database locks frequently fail under peak concurrency.

Here is the architectural blueprint we implemented across distributed Node.js/Next.js services using Redis token buckets to guarantee zero duplicate charges, sub-50ms idempotency verification, and seamless transaction recovery.

The Problem: The Duplicate Charge Trap

In West African fintech integrations (Paystack, Flutterwave, Monnify, Interswitch), network timeouts frequently occur after a customer has been debited but before the merchant server receives acknowledgment.

When the aggregator's webhook delivery engine experiences a socket hangup, its automated retry mechanism fires up to five webhook events in rapid succession.

If your webhook handler performs:

  1. Read balance
  2. Check if transaction exists in PostgreSQL
  3. Insert ledger entry
  4. Credit user balance

A race condition occurs between steps 2 and 3 if two webhook requests are processed concurrently by separate server instances.

// The race condition vulnerability:
const existing = await db.transactions.findUnique({ where: { ref } });
if (!existing) {
  // If request B arrives before request A finishes inserting, both execute!
  await db.transactions.create({ data: { ref, amount } });
  await creditUserWallet(userId, amount);
}

Solution: Atomic Distributed Locking with Redis NX

Instead of relying on database-level row locks which can cause thread pool exhaustion under heavy traffic, we place an atomic Redis lease at the edge of the API gateway.

export async function handlePaymentWebhook(req: Request) {
  const event = await req.json();
  const txRef = event.data.reference;
  const lockKey = `lock:webhook:${txRef}`;

  // Atomic set if not exists with a 30s TTL
  const acquired = await redis.set(lockKey, "PROCESSING", "NX", "EX", 30);

  if (!acquired) {
    // Return 200 OK immediately so payment aggregator ceases retries
    return Response.json({ status: "deduped", message: "Event in flight" }, { status: 200 });
  }

  try {
    await db.$transaction(async (tx) => {
      const recorded = await tx.ledger.findUnique({ where: { reference: txRef } });
      if (recorded) return;

      await tx.ledger.create({
        data: {
          reference: txRef,
          amount: event.data.amount,
          status: "SUCCESSFUL",
        },
      });

      await tx.wallet.update({
        where: { userId: event.data.customer.id },
        data: { balance: { increment: event.data.amount } },
      });
    });

    // Mark permanent processed state
    await redis.set(`processed:${txRef}`, "TRUE", "EX", 60 * 60 * 24 * 7); // 7-day retention
    return Response.json({ status: "acknowledged" });
  } finally {
    // Release atomic lock
    await redis.del(lockKey);
  }
}

Key Metrics & Production Telemetry

After rolling out this idempotent pipeline across 4 production services:

  • Zero Phantom Charges: 100% elimination of double-credit incidents.
  • P99 Verification Latency: Dropped from 340ms (database lock contention) to 42ms.
  • Retry Failure Resilience: Flawlessly handled 4x concurrent webhook bursts during peak payment spikes.
DISPATCH TAGS:#Fintech#NodeJS#Redis#DistributedSystems#Payments
Victor Okeke

Victor Okeke

DISPATCH_WRITER

PRODUCT & FULL-STACK ENGINEER // M.SC AI CANDIDATE

Building human-centered AI products at Nexenda, engineering resilient distributed applications, and actively mentoring early-career African engineering talent across Abuja and Lagos.

ENGINEERING CONSULTATION // ARCHITECTURE ADVISORY

Working on something similar? Let's talk.

Available for technical architecture advisory, engineering team workshops, and distributed product systems consulting.

Book a 20-Min Architecture Audit