stripe-sync-webhook

stripe-sync-webhook

When the user wants to create webhook handlers for stripe-sync-engine. Also use when the user mentions "webhook endpoint," "processWebhook," "stripe webhook handler," "stripe events," or "real-time sync."

0estrelas
0forks
Atualizado 1/21/2026
SKILL.md
readonlyread-only
name
stripe-sync-webhook
description

When the user wants to create webhook handlers for stripe-sync-engine. Also use when the user mentions "webhook endpoint," "processWebhook," "stripe webhook handler," "stripe events," or "real-time sync."

Stripe Sync Engine Webhook Setup

You are an expert in setting up Stripe webhook handlers that use stripe-sync-engine. Your goal is to help users create webhook endpoints that automatically sync Stripe events to their PostgreSQL database.

Initial Assessment

Before proceeding, verify:

  1. Is stripe-sync-engine set up? (see setup skill)
  2. Are migrations completed? (see migrations skill)
  3. What framework are you using? (Next.js, Hono, Deno Fresh, etc.)

Framework-Specific Implementations

Next.js App Router

Create app/api/webhooks/stripe/route.ts:

import { NextResponse } from 'next/server';
import { stripeSync } from '@/lib/stripeSync';

export async function POST(request: Request) {
  try {
    const signature = request.headers.get('stripe-signature') ?? undefined;
    const arrayBuffer = await request.arrayBuffer();
    const payload = Buffer.from(arrayBuffer);

    await stripeSync.processWebhook(payload, signature);

    return NextResponse.json({ received: true });
  } catch (error) {
    const message = error instanceof Error ? error.message : 'Unknown error';
    console.error('Webhook processing failed:', message);
    return NextResponse.json({ error: message }, { status: 400 });
  }
}

Next.js Pages Router

Create pages/api/webhooks/stripe.ts:

import type { NextApiRequest, NextApiResponse } from 'next';
import { stripeSync } from '@/lib/stripeSync';
import { buffer } from 'micro';

// Disable body parsing - we need the raw body for signature verification
export const config = {
  api: {
    bodyParser: false,
  },
};

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  try {
    const signature = req.headers['stripe-signature'] as string | undefined;
    const payload = await buffer(req);

    await stripeSync.processWebhook(payload, signature);

    return res.status(200).json({ received: true });
  } catch (error) {
    const message = error instanceof Error ? error.message : 'Unknown error';
    console.error('Webhook processing failed:', message);
    return res.status(400).json({ error: message });
  }
}

Install micro for body parsing:

npm install micro

Hono

import { Hono } from 'hono';
import { StripeSync } from 'stripe-sync-engine';

const stripeSync = new StripeSync({
  poolConfig: { connectionString: process.env.DATABASE_URL },
  stripeSecretKey: process.env.STRIPE_SECRET_KEY!,
  stripeWebhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
});

const app = new Hono();

app.post('/webhooks/stripe', async (c) => {
  const signature = c.req.header('stripe-signature') ?? undefined;
  const arrayBuffer = await c.req.raw.arrayBuffer();
  const payload = Buffer.from(arrayBuffer);

  await stripeSync.processWebhook(payload, signature);

  return c.json({ received: true });
});

Deno Fresh

Create routes/api/webhooks/stripe.ts:

import { Handlers } from "$fresh/server.ts";
import { stripeSync } from "../../../utils/stripeSync.ts";

export const handler: Handlers = {
  async POST(req) {
    try {
      const signature = req.headers.get("stripe-signature") ?? undefined;
      const payload = await req.text();

      await stripeSync.processWebhook(payload, signature);

      return new Response(
        JSON.stringify({ received: true }),
        { headers: { "Content-Type": "application/json" } }
      );
    } catch (error) {
      const message = error instanceof Error ? error.message : "Unknown error";
      console.error("Webhook processing failed:", message);
      return new Response(
        JSON.stringify({ error: message }),
        { status: 400, headers: { "Content-Type": "application/json" } }
      );
    }
  },
};

Cloudflare Workers (Forwarding Pattern)

Cloudflare Workers can't connect directly to PostgreSQL. Use a forwarding pattern:

import { Hono } from 'hono';
import Stripe from 'stripe';

type Bindings = {
  STRIPE_SECRET_KEY: string;
  STRIPE_WEBHOOK_SECRET: string;
  FORWARD_SYNC_URL: string; // URL of your sync service
};

const app = new Hono<{ Bindings: Bindings }>();

app.post('/webhooks/stripe', async (c) => {
  const { STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, FORWARD_SYNC_URL } = c.env;

  const stripe = new Stripe(STRIPE_SECRET_KEY, {
    httpClient: Stripe.createFetchHttpClient(),
  });

  const payload = await c.req.text();
  const signature = c.req.header('stripe-signature');

  if (!signature) {
    return c.json({ error: 'Missing stripe-signature header' }, 400);
  }

  // Verify signature
  let event: Stripe.Event;
  try {
    event = stripe.webhooks.constructEvent(payload, signature, STRIPE_WEBHOOK_SECRET);
  } catch (error) {
    return c.json({ error: 'Invalid Stripe signature' }, 400);
  }

  // Forward to sync service
  await fetch(FORWARD_SYNC_URL, {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
      'stripe-event-id': event.id,
    },
    body: JSON.stringify(event),
  });

  return c.json({ received: true });
});

export default app;

Configuring Stripe Dashboard

  1. Go to Stripe Dashboard > Webhooks
  2. Click Add endpoint
  3. Enter your webhook URL:
    • Development: Use Stripe CLI (see below)
    • Production: https://yourdomain.com/api/webhooks/stripe
  4. Select events to listen to (recommended: select "All events")
  5. Copy the Signing secret (whsec_...) to your environment variables

Local Development with Stripe CLI

Install and set up Stripe CLI:

# macOS
brew install stripe/stripe-cli/stripe

# Login
stripe login

# Forward webhooks to your local server
stripe listen --forward-to localhost:3000/api/webhooks/stripe

# In another terminal, trigger test events
stripe trigger payment_intent.succeeded
stripe trigger customer.created
stripe trigger invoice.paid

The CLI will show you a temporary webhook secret to use for local testing.

Event Types Processed

stripe-sync-engine automatically handles these event types:

Category Events
Customers customer.created, customer.updated, customer.deleted
Products product.created, product.updated, product.deleted
Prices price.created, price.updated, price.deleted
Subscriptions customer.subscription.* events
Invoices invoice.* events
Payments payment_intent.*, charge.* events
Disputes charge.dispute.* events
Refunds charge.refund.* events

Adding Custom Business Logic

You can add your own logic after sync completes:

export async function POST(request: Request) {
  const signature = request.headers.get('stripe-signature') ?? undefined;
  const payload = await request.arrayBuffer();

  // Sync to database
  await stripeSync.processWebhook(Buffer.from(payload), signature);

  // Parse event for custom logic
  const event = JSON.parse(new TextDecoder().decode(payload));

  switch (event.type) {
    case 'customer.subscription.created':
      // Send welcome email, provision access, etc.
      await handleNewSubscription(event.data.object);
      break;
    case 'invoice.payment_failed':
      // Send dunning email
      await handlePaymentFailure(event.data.object);
      break;
  }

  return NextResponse.json({ received: true });
}

Troubleshooting

Signature Verification Failed

  • Ensure STRIPE_WEBHOOK_SECRET matches the signing secret from Stripe Dashboard
  • For local testing, use the secret from stripe listen output
  • Ensure you're passing the raw body, not parsed JSON

Webhook Not Receiving Events

  1. Check Stripe Dashboard > Webhooks for delivery attempts
  2. Verify your endpoint URL is publicly accessible
  3. Check server logs for errors

Timeout Errors

  • stripe-sync-engine is designed to be fast, but large payloads may take longer
  • Consider increasing your serverless function timeout
  • For very high volume, consider queueing events

Related Skills

  • setup: Install and configure stripe-sync-engine
  • migrations: Create the database schema first
  • troubleshooting: Debug webhook issues

You Might Also Like

Related Skills

gog

gog

169Kdev-api

Google Workspace CLI for Gmail, Calendar, Drive, Contacts, Sheets, and Docs.

openclaw avataropenclaw
Obter
weather

weather

169Kdev-api

Get current weather and forecasts (no API key required).

openclaw avataropenclaw
Obter

Guide for implementing oRPC contract-first API patterns in Dify frontend. Triggers when creating new API contracts, adding service endpoints, integrating TanStack Query with typed contracts, or migrating legacy service calls to oRPC. Use for all API layer work in web/contract and web/service directories.

langgenius avatarlanggenius
Obter
blucli

blucli

92Kdev-api

BluOS CLI (blu) for discovery, playback, grouping, and volume.

moltbot avatarmoltbot
Obter
ordercli

ordercli

92Kdev-api

Foodora-only CLI for checking past orders and active order status (Deliveroo WIP).

moltbot avatarmoltbot
Obter
gifgrep

gifgrep

92Kdev-api

Search GIF providers with CLI/TUI, download results, and extract stills/sheets.

moltbot avatarmoltbot
Obter