FloPay
Examples

Webhook Handler

Verify and handle FloPay webhook events using the @flopay/node server SDK.

Use the @flopay/node SDK to verify webhook signatures and handle payment events in your server. The FloPay.webhooks.constructEvent method validates the payload against the Stripe webhook signing secret.

app/api/webhooks/route.ts
import { FloPay } from '@flopay/node';
import { 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: Request) {
  const payload = await request.text();
  const signature = request.headers.get('stripe-signature');
 
  if (!signature) {
    return NextResponse.json(
      { error: 'Missing stripe-signature header' },
      { status: 400 },
    );
  }
 
  let event;
 
  try {
    event = flopay.webhooks.constructEvent(payload, signature, webhookSecret);
  } catch (err) {
    console.error('Webhook signature verification failed:', err);
    return NextResponse.json(
      { error: 'Invalid signature' },
      { status: 400 },
    );
  }
 
  // Handle the event
  switch (event.type) {
    case 'checkout.session.completed': {
      const session = event.data.object;
      console.log('Checkout completed:', session);
      // Fulfill the order, activate subscription, send confirmation email
      break;
    }
 
    case 'payment_intent.succeeded': {
      const paymentIntent = event.data.object;
      console.log('Payment succeeded:', paymentIntent);
      // Record successful payment in your database
      break;
    }
 
    case 'payment_intent.payment_failed': {
      const paymentIntent = event.data.object;
      console.log('Payment failed:', paymentIntent);
      // Notify user of failed payment, retry logic
      break;
    }
 
    case 'customer.subscription.created': {
      const subscription = event.data.object;
      console.log('Subscription created:', subscription);
      // Activate subscription access for the user
      break;
    }
 
    case 'customer.subscription.deleted': {
      const subscription = event.data.object;
      console.log('Subscription cancelled:', subscription);
      // Revoke subscription access
      break;
    }
 
    case 'invoice.payment_failed': {
      const invoice = event.data.object;
      console.log('Invoice payment failed:', invoice);
      // Handle failed recurring payment
      break;
    }
 
    default:
      console.log('Unhandled event type:', event.type);
  }
 
  return NextResponse.json({ received: true });
}

Event Structure

The constructEvent method returns a WebhookEvent with the following shape:

interface WebhookEvent {
  id: string;           // Event ID (e.g. "evt_1234")
  type: string;         // Event type (e.g. "payment_intent.succeeded")
  data: Record<string, unknown>; // Event payload
  created: number;      // Unix timestamp
}

Important Notes

  • Always verify the webhook signature before processing events. Never trust raw payloads.
  • Use the raw request body (request.text()) for signature verification -- parsed JSON will not match the signature.
  • Set your webhook endpoint URL in the Stripe dashboard and note the signing secret.
  • Return a 200 response promptly. Move heavy processing to a background job to avoid webhook timeouts.

On this page