All Posts

Webhooks Explained: Don't Call Us, We'll Call You

Abstract AlgorithmsAbstract Algorithms
··5 min read

TL;DR

TLDR: Polling is like asking "Are we there yet?" every 5 seconds. Webhooks are like the driver tapping you on the shoulder when you arrive. They allow systems to communicate in real-time by sending HTTP POST requests when an event occurs, saving reso...

Cover Image for Webhooks Explained: Don't Call Us, We'll Call You

TLDR: Polling is like asking "Are we there yet?" every 5 seconds. Webhooks are like the driver tapping you on the shoulder when you arrive. They allow systems to communicate in real-time by sending HTTP POST requests when an event occurs, saving resources and reducing latency.


1. The Problem: The Inefficiency of Polling

Imagine you are building an E-commerce site. You use a 3rd-party Payment Gateway (like Stripe). After a user pays, you need to know if the payment succeeded so you can ship the order.

Approach A: Polling (The "Nagging" Method) Your server asks Stripe every 10 seconds:

  • 10:00:00 -> "Is Order #123 paid?" -> "No."
  • 10:00:10 -> "Is Order #123 paid?" -> "No."
  • ...
  • 10:05:00 -> "Is Order #123 paid?" -> "Yes."

Why this is bad:

  1. Wasted Resources: 99% of your requests return "No."
  2. Latency: If the payment succeeds at 10:00:11, you won't know until 10:00:20.
  3. Rate Limits: Stripe will block you for spamming their API.

2. The Solution: Webhooks (The "Reverse API")

Approach B: Webhooks You give Stripe a URL (https://api.myshop.com/hooks/payment). You say: "When a payment succeeds, send a POST request to this URL."

  • 10:00:00 -> User pays.
  • 10:05:00 -> Payment clears.
  • 10:05:01 -> Stripe sends POST to your URL with { "status": "success", "order_id": "123" }.

Benefit: Zero waste. Instant update.


3. System Design: When to Use Webhooks?

Use Webhooks when you need Event-Driven communication between decoupled systems.

ScenarioUse Webhook?Why?
Payment Gateways✅ YesPayments take time (seconds to days). You can't hold a connection open.
CI/CD Pipelines✅ YesGitHub notifies Jenkins when code is pushed.
Chat Bots✅ YesSlack notifies your bot when a user types a message.
Real-time Search❌ NoUse WebSockets for bi-directional, low-latency streams.
Fetching User Profile❌ NoJust use a standard REST GET request.

Visualizing the Flow

sequenceDiagram
    participant User
    participant MyServer
    participant Stripe

    User->>MyServer: Checkout
    MyServer->>Stripe: Create Payment Intent
    Stripe-->>MyServer: Payment URL
    MyServer-->>User: Redirect to Stripe

    User->>Stripe: Enters Card Details
    Note over Stripe: Processing... (Takes time)

    Stripe->>MyServer: POST /hooks/payment (Webhook)
    MyServer-->>Stripe: 200 OK

    Note over MyServer: Ship Order

Deep Dive: How a Webhook Differs from a Standard POST

Technically, a webhook is just a POST request. But functionally, there are two critical differences you must handle:

1. The Response Contract ("Ack" vs. "Result")

  • Standard API: You send data, wait for processing, and get a result (e.g., Created User ID: 55).
  • Webhook: The sender doesn't care about your business logic result. They only want to know "Did you receive this?"
    • Rule: Always return 200 OK immediately after validating the signature. Do not make the sender wait for you to send emails or update databases.

2. Schema Inversion

  • Standard API: You (the server) define the JSON schema. Clients must follow it.
  • Webhook: The Sender (Stripe) defines the JSON schema. You (the server) must adapt your code to match their format.

4. Implementation: Spring Boot Webhook Listener

Receiving a webhook is just like writing a standard REST Controller, but with extra security checks.

Step 1: The Controller

@RestController
@RequestMapping("/api/webhooks")
public class StripeWebhookController {

    @PostMapping("/payment")
    public ResponseEntity<String> handlePaymentEvent(
            @RequestBody String payload, 
            @RequestHeader("Stripe-Signature") String sigHeader) {

        // 1. Verify the Event (Security is Critical!)
        if (!verifySignature(payload, sigHeader)) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }

        // 2. Parse the JSON
        PaymentEvent event = parseEvent(payload);

        // 3. Handle Business Logic
        if ("payment.succeeded".equals(event.getType())) {
            shipOrder(event.getOrderId());
        }

        // 4. Acknowledge Receipt (Important!)
        return ResponseEntity.ok("Received");
    }
}

Step 2: Security (The Secret Handshake)

Anyone can send a POST request to your URL. How do you know it's actually Stripe?

  • Shared Secret: Stripe gives you a secret key (whsec_...).
  • Hashing: Stripe hashes the payload with this key and sends the hash in the header (Stripe-Signature).
  • Verification: You hash the received payload with your copy of the key. If the hashes match, the data is authentic.

5. Best Practices for Production

  1. Idempotency: Stripe might send the same webhook twice (retries). Ensure your code handles duplicates (e.g., check if (order.isPaid()) return;).
  2. Respond Quickly: Do heavy processing asynchronously.
    • Bad: Receive Webhook -> Generate PDF -> Email User -> Return 200 OK. (Stripe will timeout).
    • Good: Receive Webhook -> Push to Queue -> Return 200 OK. Worker processes Queue.
  3. Retries: If your server is down, the sender should retry (Exponential Backoff).

Summary & Key Takeaways

  • Webhooks are "User-Defined HTTP Callbacks".
  • Polling vs. Webhooks: Polling is pulling; Webhooks are pushing.
  • Security: Always verify the signature to prevent spoofing.
  • Design: Use for asynchronous, event-driven updates (Payments, CI/CD).

Practice Quiz: Test Your Design Skills

  1. Scenario: You are building a dashboard that shows live stock prices updating every millisecond. Should you use Webhooks?

    • A) Yes, because it's real-time.
    • B) No, use WebSockets. Webhooks have HTTP overhead and are better for discrete events, not high-frequency streams.
    • C) Yes, but only with a fast server.
  2. Scenario: Your webhook listener crashes while processing an event. The sender (e.g., Stripe) receives a 500 Error. What usually happens next?

    • A) The event is lost forever.
    • B) The sender retries sending the event later (Exponential Backoff).
    • C) The sender deletes your account.
  3. Scenario: Why is "Idempotency" important in webhook handlers?

    • A) To make the code run faster.
    • B) To prevent processing the same event twice (e.g., shipping an order twice) if the sender retries.
    • C) To encrypt the data.

(Answers: 1-B, 2-B, 3-B)

Abstract Algorithms

Written by

Abstract Algorithms

@abstractalgorithms