FloPay
Guides

Webhooks

Verify and handle payment webhooks with the FloPay Node SDK.

Webhooks

The @flopay/node SDK provides webhooks.constructEvent to verify Stripe webhook signatures and parse event payloads.

Setup

You need:

  • A Stripe webhook endpoint secret (whsec_...)
  • The raw request body (as a string or Buffer -- not parsed JSON)
  • The Stripe-Signature header from the incoming request

Next.js API Route Example

// app/api/webhooks/route.ts
import { FloPay } from '@flopay/node';
import { NextRequest, NextResponse } from 'next/server';
 
const flopay = new FloPay(process.env.STRIPE_SECRET_KEY!);
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;
 
export async function POST(request: NextRequest) {
  const body = await request.text();
  const signature = request.headers.get('stripe-signature');
 
  if (!signature) {
    return NextResponse.json({ error: 'Missing signature' }, { status: 400 });
  }
 
  let event;
  try {
    event = flopay.webhooks.constructEvent(body, signature, webhookSecret);
  } catch (err) {
    console.error('Webhook signature verification failed:', err);
    return NextResponse.json({ error: 'Invalid signature' }, { status: 400 });
  }
 
  switch (event.type) {
    case 'checkout.session.completed':
      const session = event.data.object;
      // Fulfill the order
      console.log('Checkout completed:', session.id);
      break;
 
    case 'payment_intent.succeeded':
      const paymentIntent = event.data.object;
      console.log('Payment succeeded:', paymentIntent.id);
      break;
 
    case 'payment_intent.payment_failed':
      const failedIntent = event.data.object;
      console.error('Payment failed:', failedIntent.id);
      break;
 
    default:
      console.log('Unhandled event type:', event.type);
  }
 
  return NextResponse.json({ received: true });
}

You must read the request body as raw text (request.text()), not parsed JSON. Stripe signature verification requires the exact bytes that were sent. If your framework parses the body automatically, configure it to skip parsing for your webhook route.

Express Example

import express from 'express';
import { FloPay } from '@flopay/node';
 
const app = express();
const flopay = new FloPay(process.env.STRIPE_SECRET_KEY!);
 
// Use raw body parser for webhook route
app.post(
  '/api/webhooks',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const signature = req.headers['stripe-signature'] as string;
 
    try {
      const event = flopay.webhooks.constructEvent(
        req.body,
        signature,
        process.env.STRIPE_WEBHOOK_SECRET!,
      );
 
      // Handle event...
      console.log('Received:', event.type);
      res.json({ received: true });
    } catch (err) {
      console.error('Webhook error:', err);
      res.status(400).send('Webhook Error');
    }
  },
);

Testing Locally

Use the Stripe CLI to forward events to your local server:

stripe listen --forward-to localhost:3000/api/webhooks

The CLI prints a webhook signing secret (whsec_...) to use during development.

On this page