Next.js template

Reliable Stripe webhooks, ready to run

A working Next.js example that verifies Stripe events, queues them through RocketStack, and processes them asynchronously in a Function.

Stripe webhookQueueFunction

Architecture

StripeNext.js routeQueueFunction

Source path: examples/nextjs-stripe-webhooks

What the template includes

  • Next.js webhook route

    App Router handler at POST /api/webhooks/stripe.

  • Stripe signature verification

    Uses the raw request body and Stripe’s official server library.

  • RocketStack Queue integration

    Enqueues a concise payload with the Stripe event ID as idempotencyKey.

  • RocketStack processing Function

    Handles common Stripe event types asynchronously after enqueue.

  • Environment-variable template

    Server-side secrets only — no NEXT_PUBLIC_ API keys.

  • Local Stripe CLI testing guide

    Forward events locally and verify Queue + Function delivery.

Architecture

Stripe
  |
  v
Next.js webhook route
  |
  v
RocketStack Queue
  |
  v
RocketStack Function

Key code excerpts

Verify the signature

Verify the signature
export async function POST(request: Request) {
  const signature = request.headers.get('stripe-signature')
  if (!signature) {
    return Response.json(
      { error: 'Missing Stripe-Signature header' },
      { status: 400 }
    )
  }

  let event
  try {
    const rawBody = await request.text()
    event = constructStripeEvent(rawBody, signature)
  } catch {
    return Response.json(
      { error: 'Invalid Stripe signature' },
      { status: 400 }
    )
  }

  try {
    const payload = toQueuePayload(event)
    await enqueueStripeEvent(payload)
    return new Response(null, { status: 202 })
  } catch (error) {
    return Response.json(
      { error: 'Unable to enqueue event' },
      { status: 500 }
    )
  }
}

Add the event to Queue

Add the event to Queue
export async function enqueueStripeEvent(
  payload: StripeQueuePayload
): Promise<{ messageId: string; status: string }> {
  const queueId = requiredEnv('ROCKETSTACK_QUEUE_ID')
  const queues = getQueuesClient()

  const response = await queues.enqueueMessage(queueId, {
    payload,
    idempotencyKey: payload.eventId
  })

  const data = response.data?.data
  if (!data?.messageId) {
    throw new Error('Queue enqueue returned an unexpected response')
  }

  return {
    messageId: data.messageId,
    status: data.status
  }
}

Process the queued event

Process the queued event
exports.handler = async function handler(event) {
  const payload = event?.payload
  if (!payload?.eventId || !payload?.eventType) {
    throw new Error('Invalid queue delivery payload')
  }

  const key = processedKey(payload.eventId)
  const processedStore = createKvProcessedEventStore()
  if (processedStore && await processedStore.hasProcessed(payload.eventId)) {
    return {
      success: true,
      eventId: payload.eventId,
      eventType: payload.eventType,
      processedAt: new Date().toISOString(),
      skipped: true
    }
  }

  await applyApplicationLogic(payload)

  if (processedStore) {
    await processedStore.markProcessed(payload.eventId)
  }

  return {
    success: true,
    eventId: payload.eventId,
    eventType: payload.eventType,
    processedAt: new Date().toISOString()
  }
}

Setup steps

Open the README
  1. Step 1

    Clone the example

    Copy examples/nextjs-stripe-webhooks and install dependencies.

  2. Step 2

    Add environment variables

    Set STRIPE_WEBHOOK_SECRET, ROCKETSTACK_API_KEY, and ROCKETSTACK_QUEUE_ID.

  3. Step 3

    Configure Queue and Function

    Deploy process-stripe-event and point the Queue delivery target at it.

  4. Step 4

    Forward a Stripe test webhook

    Use stripe listen and stripe trigger to exercise the full path.

Full install, Stripe CLI, and troubleshooting steps are in examples/nextjs-stripe-webhooks/README.md.

How duplicate events are handled

  • Queue idempotencyKey

    The webhook route sets idempotencyKey to the Stripe event ID so duplicate deliveries reuse the same queue message when accepted by Queue.

  • Optional KV marker

    The Function includes a demo ProcessedEventStore backed by RocketStack KV. KV supports conditional writes via ifVersion, but not a simple create-if-absent claim.

  • Application database

    For irreversible side effects, persist processed event IDs in your own database. This template does not claim exactly-once processing.

Production considerations

  • Keep Stripe and RocketStack secrets server-side only
  • Queue only the fields you need — avoid full payment payloads
  • Make Function side effects safe to run more than once
  • Monitor failed deliveries and dead-lettered messages in the console
  • Point Stripe’s production webhook at your deployed route
  • Review Free plan usage limits before going live

Queue documentation · Functions documentation · Pricing

Run the template with RocketStack

Create a free account, get an API key, and connect the Queue and Function used by the example.