<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Aissatou Lamarana Diallo</title>
    <description>The latest articles on DEV Community by Aissatou Lamarana Diallo (@aissatou_lamaranadiallo_).</description>
    <link>https://dev.to/aissatou_lamaranadiallo_</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4136041%2F8fb57175-5047-406e-b19a-c4221284e580.jpg</url>
      <title>DEV Community: Aissatou Lamarana Diallo</title>
      <link>https://dev.to/aissatou_lamaranadiallo_</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aissatou_lamaranadiallo_"/>
    <language>en</language>
    <item>
      <title>I built a tool to stop losing sleep over silently failing payment webhooks (Flutterwave/Paystack/Orange Money)</title>
      <dc:creator>Aissatou Lamarana Diallo</dc:creator>
      <pubDate>Mon, 21 Sep 2026 15:58:22 +0000</pubDate>
      <link>https://dev.to/aissatou_lamaranadiallo_/i-built-a-tool-to-stop-losing-sleep-over-silently-failing-payment-webhooks-1jb9</link>
      <guid>https://dev.to/aissatou_lamaranadiallo_/i-built-a-tool-to-stop-losing-sleep-over-silently-failing-payment-webhooks-1jb9</guid>
      <description>&lt;p&gt;If you've ever integrated Flutterwave, Paystack, or Orange Money into a product, you know the drill: the payment succeeds, the provider tries to call your webhook endpoint to confirm it, your server hiccups for two seconds (deploy, cold start, whatever), and the callback is gone. The customer paid. Your database doesn't know it. You find out when they complain.&lt;/p&gt;

&lt;p&gt;I got tired of this happening on projects I was building, so I built Webhook Watch: a small proxy that sits in front of your real webhook endpoint and makes sure you never lose a delivery silently again.&lt;/p&gt;

&lt;p&gt;How it works&lt;/p&gt;

&lt;p&gt;Instead of pointing your payment provider at your own endpoint, you point it at a Webhook Watch URL. That URL:&lt;/p&gt;

&lt;p&gt;Logs the full request (headers, body, method) before doing anything else&lt;br&gt;
Forwards it to your real endpoint&lt;br&gt;
Records the response status and latency&lt;br&gt;
If your endpoint fails or times out, retries automatically with backoff&lt;br&gt;
Deduplicates retried events from the provider itself, using the delivery ID / idempotency key it sends (Flutterwave and most payment providers retry on their own when they don't get a 2xx  without dedup you'd double-process a payment)&lt;br&gt;
Lets you manually replay any specific event from a dashboard if you fixed the bug that caused the failure&lt;/p&gt;

&lt;p&gt;Why edge runtime&lt;/p&gt;

&lt;p&gt;The receiving endpoint (/api/hook/[slug]) runs on Next.js Edge Runtime rather than a normal serverless function. Under a burst of webhooks  a payment provider retrying aggressively, or just real traffic  a slow cold start is exactly when you can't afford to drop something. Edge functions spin up faster and don't share the same cold-start profile as regular Node functions on most platforms.&lt;/p&gt;

&lt;p&gt;Why idempotency mattered more than I expected&lt;/p&gt;

&lt;p&gt;Early on I just logged and forwarded. Then I realized: if my endpoint returns a 500, the provider retries the same event a few minutes later and if I "fixed" it by then, I'd process the same payment twice. So every delivery gets a dedup key extracted from known provider headers (X-GitHub-Delivery, Webhook-Id, etc.) or falls back to a payload id field, enforced with a partial unique index in Postgres. A duplicate delivery gets short-circuited instead of re-forwarded.&lt;/p&gt;

&lt;p&gt;What it's built on&lt;/p&gt;

&lt;p&gt;Next.js 14 App Router, Supabase (Postgres + Auth + Realtime  the dashboard updates live when a new event comes in), deployed on Vercel.&lt;/p&gt;

&lt;p&gt;Where it's at&lt;/p&gt;

&lt;p&gt;It's live, solo-built, free to start (1 endpoint, 200 events/month, no card required): &lt;a href="https://webhook-watch.vercel.app" rel="noopener noreferrer"&gt;https://webhook-watch.vercel.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're dealing with payment webhooks  especially in markets where the provider's own reliability is inconsistent  I'd genuinely like to know if this solves a real problem for you or if I'm missing something obvious. Happy to answer questions about the architecture in the comments.&lt;/p&gt;

&lt;h1&gt;
  
  
  webdev #showdev #api #payments
&lt;/h1&gt;

</description>
      <category>api</category>
      <category>backend</category>
      <category>buildinpublic</category>
      <category>tools</category>
    </item>
  </channel>
</rss>
