Verifying Webhook Signatures
typescript
// app/api/webhooks/settlesettle/route.ts (Next.js App Router)
import crypto from 'crypto'
import { NextRequest, NextResponse } from 'next/server'
export async function POST(req: NextRequest) {
// Step 1: Get the raw body as a Buffer BEFORE any parsing
const rawBody = await req.arrayBuffer()
const bodyBuffer = Buffer.from(rawBody)
// Step 2: Get the signature from the header
const signature = req.headers.get('x-paystack-signature')
if (!signature) {
return NextResponse.json({ error: 'Missing signature' }, { status: 401 })
}
// Step 3: Compute the expected signature
const expectedSignature = crypto
.createHmac('sha512', process.env.PAYSTACK_WEBHOOK_SECRET!)
.update(bodyBuffer)
.digest('hex')
// Step 4: Compare — constant-time comparison prevents timing attacks
if (signature !== expectedSignature) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 })
}
// Step 5: Parse and handle
const event = JSON.parse(bodyBuffer.toString())
switch (event.event) {
case 'charge.success':
// Payment confirmed — SettleSettle handles the credit automatically
// Update your own UI state here if needed
break
default:
// Unknown event type — ignore safely
}
// Step 6: Always return 200 — or SettleSettle will retry
return NextResponse.json({ received: true })
}