Skip to content

Webhook Processing Example

A resilient way to process incoming webhooks from external providers like Stripe, GitHub, or Shopify.

The Scenario

  1. Parse & Validate: Ensure the webhook is authentic and well-formed.
  2. Idempotency Check: Ensure we don't process the same webhook ID twice.
  3. Core Logic: Update our local state based on the webhook payload.
  4. Sync Downstream: Notify other internal services.

The Code

ts
import { create, createIdempotencyStore } from "@eddiecbrl/orchestrix";

const store = createIdempotencyStore(); // Use Redis in production!

const webhookFlow = create("stripe-webhook", { idempotency: store })
  
  .step("validate-signature", (ctx) => {
    const { payload, signature } = ctx.input;
    const isValid = stripe.verify(payload, signature);
    if (!isValid) throw new Error("Invalid signature");
  })

  .step("update-subscription", async (ctx) => {
    const { data } = ctx.input;
    await db.subscriptions.update(data.customer, { status: data.status });
  }, {
    retries: 3,
    timeoutMs: 2000
  })

  .step("notify-crm", async (ctx) => {
    await crmService.updateContact(ctx.input.data.customer, { lastBillingStatus: 'ok' });
  });

// Express/Fastify handler
app.post('/webhooks/stripe', async (req, res) => {
  const stripeEventId = req.body.id;

  const result = await webhookFlow.run(req.body, {
    key: `webhook:${stripeEventId}`,
    ttlMs: 24 * 60 * 60 * 1000 // Keep for 24 hours
  });

  if (result.status === 'completed') {
    res.status(200).send("Processed");
  } else {
    res.status(500).send("Error");
  }
});

Highlights

  • Native Idempotency: Webhook providers often send the same event multiple times. Orchestrix's idempotency handles this automatically.
  • Fail-Safe: If notify-crm fails, the webhook has already updated our core subscription state. We can use retries to make notify-crm more reliable.
  • Observability: Using hooks, you can track how many webhooks are being retried or failing, providing early warning for integration issues.

Released under the MIT License.